﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Collections;
using System.IO;
using System.Security;
using System.Security.Permissions;
using System.Net;
using System.Configuration.Assemblies;
using Hack.Security.Cryptography;
using Hack.Security.Cryptography.Algorythms;

namespace Hack.Security.X509
{
    internal sealed class BitConverterLE
    {
        private BitConverterLE()
        {
        }

        unsafe private static byte[] GetUShortBytes(byte* bytes)
        {
            if (BitConverter.IsLittleEndian)
                return new byte[] { bytes[0], bytes[1] };
            else
                return new byte[] { bytes[1], bytes[0] };
        }

        unsafe private static byte[] GetUIntBytes(byte* bytes)
        {
            if (BitConverter.IsLittleEndian)
                return new byte[] { bytes[0], bytes[1], bytes[2], bytes[3] };
            else
                return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
        }

        unsafe private static byte[] GetULongBytes(byte* bytes)
        {
            if (BitConverter.IsLittleEndian)
                return new byte[] { bytes [0], bytes [1], bytes [2], bytes [3],
						     bytes [4], bytes [5], bytes [6], bytes [7] };
            else
                return new byte[] { bytes [7], bytes [6], bytes [5], bytes [4],
						     bytes [3], bytes [2], bytes [1], bytes [0] };
        }

        unsafe internal static byte[] GetBytes(bool value)
        {
            return new byte[] { value ? (byte)1 : (byte)0 };
        }

        unsafe internal static byte[] GetBytes(char value)
        {
            return GetUShortBytes((byte*)&value);
        }

        unsafe internal static byte[] GetBytes(short value)
        {
            return GetUShortBytes((byte*)&value);
        }

        unsafe internal static byte[] GetBytes(int value)
        {
            return GetUIntBytes((byte*)&value);
        }

        unsafe internal static byte[] GetBytes(long value)
        {
            return GetULongBytes((byte*)&value);
        }

        unsafe internal static byte[] GetBytes(ushort value)
        {
            return GetUShortBytes((byte*)&value);
        }

        unsafe internal static byte[] GetBytes(uint value)
        {
            return GetUIntBytes((byte*)&value);
        }

        unsafe internal static byte[] GetBytes(ulong value)
        {
            return GetULongBytes((byte*)&value);
        }

        unsafe internal static byte[] GetBytes(float value)
        {
            return GetUIntBytes((byte*)&value);
        }

        unsafe internal static byte[] GetBytes(double value)
        {
            return GetULongBytes((byte*)&value);
        }

        unsafe private static void UShortFromBytes(byte* dst, byte[] src, int startIndex)
        {
            if (BitConverter.IsLittleEndian)
            {
                dst[0] = src[startIndex];
                dst[1] = src[startIndex + 1];
            }
            else
            {
                dst[0] = src[startIndex + 1];
                dst[1] = src[startIndex];
            }
        }

        unsafe private static void UIntFromBytes(byte* dst, byte[] src, int startIndex)
        {
            if (BitConverter.IsLittleEndian)
            {
                dst[0] = src[startIndex];
                dst[1] = src[startIndex + 1];
                dst[2] = src[startIndex + 2];
                dst[3] = src[startIndex + 3];
            }
            else
            {
                dst[0] = src[startIndex + 3];
                dst[1] = src[startIndex + 2];
                dst[2] = src[startIndex + 1];
                dst[3] = src[startIndex];
            }
        }

        unsafe private static void ULongFromBytes(byte* dst, byte[] src, int startIndex)
        {
            if (BitConverter.IsLittleEndian)
            {
                for (int i = 0; i < 8; ++i)
                    dst[i] = src[startIndex + i];
            }
            else
            {
                for (int i = 0; i < 8; ++i)
                    dst[i] = src[startIndex + (7 - i)];
            }
        }

        unsafe internal static bool ToBoolean(byte[] value, int startIndex)
        {
            return value[startIndex] != 0;
        }

        unsafe internal static char ToChar(byte[] value, int startIndex)
        {
            char ret;

            UShortFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }

        unsafe internal static short ToInt16(byte[] value, int startIndex)
        {
            short ret;

            UShortFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }

        unsafe internal static int ToInt32(byte[] value, int startIndex)
        {
            int ret;

            UIntFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }

        unsafe internal static long ToInt64(byte[] value, int startIndex)
        {
            long ret;

            ULongFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }

        unsafe internal static ushort ToUInt16(byte[] value, int startIndex)
        {
            ushort ret;

            UShortFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }

        unsafe internal static uint ToUInt32(byte[] value, int startIndex)
        {
            uint ret;

            UIntFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }

        unsafe internal static ulong ToUInt64(byte[] value, int startIndex)
        {
            ulong ret;

            ULongFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }

        unsafe internal static float ToSingle(byte[] value, int startIndex)
        {
            float ret;

            UIntFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }

        unsafe internal static double ToDouble(byte[] value, int startIndex)
        {
            double ret;

            ULongFromBytes((byte*)&ret, value, startIndex);

            return ret;
        }
    }

    // convert an XML document into SecurityElement objects
    internal class SecurityParser : MiniParser, MiniParser.IHandler, MiniParser.IReader
    {

        private SecurityElement root;

        public SecurityParser()
            : base()
        {
            stack = new Stack();
        }

        public void LoadXml(string xml)
        {
            root = null;
            xmldoc = xml;
            pos = 0;
            stack.Clear();
            Parse(this, this);
        }

        public SecurityElement ToXml()
        {
            return root;
        }

        // IReader

        private string xmldoc;
        private int pos;

        public int Read()
        {
            if (pos >= xmldoc.Length)
                return -1;
            return (int)xmldoc[pos++];
        }

        // IHandler

        private SecurityElement current;
        private Stack stack;

        public void OnStartParsing(MiniParser parser) { }

        public void OnStartElement(string name, MiniParser.IAttrList attrs)
        {
            SecurityElement newel = new SecurityElement(name);
            if (root == null)
            {
                root = newel;
                current = newel;
            }
            else
            {
                SecurityElement parent = (SecurityElement)stack.Peek();
                parent.AddChild(newel);
            }
            stack.Push(newel);
            current = newel;
            // attributes
            int n = attrs.Length;
            for (int i = 0; i < n; i++)
                current.AddAttribute(attrs.GetName(i), attrs.GetValue(i));
        }

        public void OnEndElement(string name)
        {
            current = (SecurityElement)stack.Pop();
        }

        public void OnChars(string ch)
        {
            current.Text = ch;
        }

        public void OnEndParsing(MiniParser parser) { }
    }

    internal class MiniParser
    {

        public interface IReader
        {
            int Read();
        }

        public interface IAttrList
        {
            int Length { get; }
            bool IsEmpty { get; }
            string GetName(int i);
            string GetValue(int i);
            string GetValue(string name);
            void ChangeValue(string name, string newValue);
            string[] Names { get; }
            string[] Values { get; }
        }

        public interface IMutableAttrList : IAttrList
        {
            void Clear();
            void Add(string name, string value);
            void CopyFrom(IAttrList attrs);
            void Remove(int i);
            void Remove(string name);
        }

        public interface IHandler
        {
            void OnStartParsing(MiniParser parser);
            void OnStartElement(string name, IAttrList attrs);
            void OnEndElement(string name);
            void OnChars(string ch);
            void OnEndParsing(MiniParser parser);
        }

        public class HandlerAdapter : IHandler
        {
            public HandlerAdapter() { }
            public void OnStartParsing(MiniParser parser) { }
            public void OnStartElement(string name, IAttrList attrs) { }
            public void OnEndElement(string name) { }
            public void OnChars(string ch) { }
            public void OnEndParsing(MiniParser parser) { }
        }

        private enum CharKind : byte
        {
            LEFT_BR = 0,
            RIGHT_BR = 1,
            SLASH = 2,
            PI_MARK = 3,
            EQ = 4,
            AMP = 5,
            SQUOTE = 6,
            DQUOTE = 7,
            BANG = 8,
            LEFT_SQBR = 9,
            SPACE = 0xA,
            RIGHT_SQBR = 0xB,
            TAB = 0xC,
            CR = 0xD,
            EOL = 0xE,
            CHARS = 0xF,
            UNKNOWN = 0x1F
        }

        private enum ActionCode : byte
        {
            START_ELEM = 0,
            END_ELEM = 1,
            END_NAME = 2,
            SET_ATTR_NAME = 3,
            SET_ATTR_VAL = 4,
            SEND_CHARS = 5,
            START_CDATA = 6,
            END_CDATA = 7,
            ERROR = 8,
            STATE_CHANGE = 9,
            FLUSH_CHARS_STATE_CHANGE = 0xA,
            ACC_CHARS_STATE_CHANGE = 0xB,
            ACC_CDATA = 0xC,
            PROC_CHAR_REF = 0xD,
            UNKNOWN = 0xF
        }

        public class AttrListImpl : IMutableAttrList
        {
            protected ArrayList names;
            protected ArrayList values;

            public AttrListImpl() : this(0) { }

            public AttrListImpl(int initialCapacity)
            {
                if (initialCapacity <= 0)
                {
                    names = new ArrayList();
                    values = new ArrayList();
                }
                else
                {
                    names = new ArrayList(initialCapacity);
                    values = new ArrayList(initialCapacity);
                }
            }

            public AttrListImpl(IAttrList attrs)
                : this(attrs != null ? attrs.Length : 0)
            {
                if (attrs != null) this.CopyFrom(attrs);
            }

            public int Length
            {
                get { return names.Count; }
            }

            public bool IsEmpty
            {
                get { return this.Length != 0; }
            }

            public string GetName(int i)
            {
                string res = null;
                if (i >= 0 && i < this.Length)
                {
                    res = names[i] as string;
                }
                return res;
            }

            public string GetValue(int i)
            {
                string res = null;
                if (i >= 0 && i < this.Length)
                {
                    res = values[i] as string;
                }
                return res;
            }

            public string GetValue(string name)
            {
                return this.GetValue(names.IndexOf(name));
            }

            public void ChangeValue(string name, string newValue)
            {
                int i = names.IndexOf(name);
                if (i >= 0 && i < this.Length)
                {
                    values[i] = newValue;
                }
            }

            public string[] Names
            {
                get { return names.ToArray(typeof(string)) as string[]; }
            }

            public string[] Values
            {
                get { return values.ToArray(typeof(string)) as string[]; }
            }

            public void Clear()
            {
                names.Clear();
                values.Clear();
            }

            public void Add(string name, string value)
            {
                names.Add(name);
                values.Add(value);
            }

            public void Remove(int i)
            {
                if (i >= 0)
                {
                    names.RemoveAt(i);
                    values.RemoveAt(i);
                }
            }

            public void Remove(string name)
            {
                this.Remove(names.IndexOf(name));
            }

            public void CopyFrom(IAttrList attrs)
            {
                if (attrs != null && ((object)this == (object)attrs))
                {
                    this.Clear();
                    int n = attrs.Length;
                    for (int i = 0; i < n; i++)
                    {
                        this.Add(attrs.GetName(i), attrs.GetValue(i));
                    }
                }
            }
        }

        public class XMLError : Exception
        {
            protected string descr;
            protected int line, column;
            public XMLError() : this("Unknown") { }
            public XMLError(string descr) : this(descr, -1, -1) { }
            public XMLError(string descr, int line, int column)
                : base(descr)
            {
                this.descr = descr;
                this.line = line;
                this.column = column;
            }
            public int Line { get { return line; } }
            public int Column { get { return column; } }
            public override string ToString()
            {
                return (String.Format("{0} @ (line = {1}, col = {2})", descr, line, column));
            }
        }

        private static readonly int INPUT_RANGE = 13;
        private static readonly ushort[] tbl = {
		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 1), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 128), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 128),
		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 133), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 16), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.FLUSH_CHARS_STATE_CHANGE << 8) | 4),
		(ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.END_ELEM << 8) | 0), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 2), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
		(ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 5), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3),
		(ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 4), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.END_NAME << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.END_NAME << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.END_NAME << 8) | 8), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
		(ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 3),
		(ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.FLUSH_CHARS_STATE_CHANGE << 8) | 1), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 10), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 7), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10),
		(ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_ELEM << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.START_ELEM << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 8), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
		(ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.SET_ATTR_NAME << 8) | 11), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 12), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130),
		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 13), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 10), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 10),
		(ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 11), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 132), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 132),
		(ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.SET_ATTR_NAME << 8) | 11), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 12), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 130), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 130),
		(ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.SEND_CHARS << 8) | 2), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 16), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 134), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ERROR << 8) | 134),
		(ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.SET_ATTR_VAL << 8) | 17), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 14), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 14),
		(ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.SET_ATTR_VAL << 8) | 17), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.PROC_CHAR_REF << 8) | 15), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 15),
		(ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 18), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 0), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.START_CDATA << 8) | 19),
		(ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.START_ELEM << 8) | 6), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.START_ELEM << 8) | 7), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.STATE_CHANGE << 8) | 17), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CHARS_STATE_CHANGE << 8) | 9), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ERROR << 8) | 129), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ERROR << 8) | 129),
		(ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.END_CDATA << 8) | 10), (ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 18),
		(ushort)(((ushort)CharKind.LEFT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SLASH << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_BR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.PI_MARK << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.EQ << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.AMP << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.BANG << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.LEFT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.SPACE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.RIGHT_SQBR << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.DQUOTE << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19), (ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.ACC_CDATA << 8) | 19),
		(ushort)(((ushort)CharKind.CHARS << 12) | ((ushort)ActionCode.UNKNOWN << 8) | 255),
		0xFFFF
	};

        protected static string[] errors = {
		/* 0 */ "Expected element",
		/* 1 */ "Invalid character in tag",
		/* 2 */ "No '='",
		/* 3 */ "Invalid character entity",
		/* 4 */ "Invalid attr value",
		/* 5 */ "Empty tag",
		/* 6 */ "No end tag",
		/* 7 */ "Bad entity ref"
	};

        protected int line;
        protected int col;
        protected int[] twoCharBuff;
        protected bool splitCData;

        public MiniParser()
        {
            twoCharBuff = new int[2];
            splitCData = false;
            Reset();
        }

        public void Reset()
        {
            line = 0;
            col = 0;
        }

        protected static bool StrEquals(string str, StringBuilder sb, int sbStart, int len)
        {
            if (len != str.Length) return false;
            for (int i = 0; i < len; i++)
            {
                if (str[i] != sb[sbStart + i]) return false;
            }
            return true;
        }

        protected void FatalErr(string descr)
        {
            throw new XMLError(descr, this.line, this.col);
        }

        protected static int Xlat(int charCode, int state)
        {
            int p = state * INPUT_RANGE;
            int n = System.Math.Min(tbl.Length - p, INPUT_RANGE);
            for (; --n >= 0; )
            {
                ushort code = tbl[p];
                if (charCode == (code >> 12)) return (code & 0xFFF);
                p++;
            }
            return 0xFFF;
        }

        public void Parse(IReader reader, IHandler handler)
        {
            if (reader == null) throw new ArgumentNullException("reader");
            if (handler == null) handler = new HandlerAdapter();

            AttrListImpl attrList = new AttrListImpl();
            string lastAttrName = null;
            Stack tagStack = new Stack();
            string elementName = null;
            line = 1;
            col = 0;
            int currCh = 0;
            int stateCode = 0;
            StringBuilder sbChars = new StringBuilder();
            bool seenCData = false;
            bool isComment = false;
            bool isDTD = false;
            int bracketSwitch = 0;

            handler.OnStartParsing(this);

            while (true)
            {
                ++this.col;
                int prevCh = currCh;

                currCh = reader.Read();

                if (currCh == -1)
                {
                    if (stateCode != 0)
                    {
                        FatalErr("Unexpected EOF");
                    }
                    break;
                }

                int charCode = "<>/?=&'\"![ ]\t\r\n".IndexOf((char)currCh) & 0xF;
                if (charCode == (int)CharKind.CR) continue; // ignore
                // whitepace ::= (#x20 | #x9 | #xd | #xa)+
                if (charCode == (int)CharKind.TAB) charCode = (int)CharKind.SPACE; // tab == space
                if (charCode == (int)CharKind.EOL)
                {
                    this.col = 0;
                    this.line++;
                    charCode = (int)CharKind.SPACE;
                }

                int actionCode = MiniParser.Xlat(charCode, stateCode);
                stateCode = actionCode & 0xFF;
                // Ignore newline inside attribute value.
                if (currCh == '\n' && (stateCode == 0xE || stateCode == 0xF)) continue;
                actionCode >>= 8;

                if (stateCode >= 0x80)
                {
                    if (stateCode == 0xFF)
                    {
                        FatalErr("State dispatch error.");
                    }
                    else
                    {
                        FatalErr(errors[stateCode ^ 0x80]);
                    }
                }

                switch (actionCode)
                {
                    case (int)ActionCode.START_ELEM:
                        handler.OnStartElement(elementName, attrList);
                        if (currCh != '/')
                        {
                            tagStack.Push(elementName);
                        }
                        else
                        {
                            handler.OnEndElement(elementName);
                        }
                        attrList.Clear();
                        break;

                    case (int)ActionCode.END_ELEM:
                        elementName = sbChars.ToString();
                        sbChars = new StringBuilder();
                        string endName = null;
                        if (tagStack.Count == 0 ||
                            elementName != (endName = tagStack.Pop() as string))
                        {
                            if (endName == null)
                            {
                                FatalErr("Tag stack underflow");
                            }
                            else
                            {
                                FatalErr(String.Format("Expected end tag '{0}' but found '{1}'", elementName, endName));
                            }
                        }
                        handler.OnEndElement(elementName);
                        break;

                    case (int)ActionCode.END_NAME:
                        elementName = sbChars.ToString();
                        sbChars = new StringBuilder();
                        if (currCh != '/' && currCh != '>') break;
                        goto case (int)ActionCode.START_ELEM;

                    case (int)ActionCode.SET_ATTR_NAME:
                        lastAttrName = sbChars.ToString();
                        sbChars = new StringBuilder();
                        break;

                    case (int)ActionCode.SET_ATTR_VAL:
                        if (lastAttrName == null) FatalErr("Internal error.");
                        attrList.Add(lastAttrName, sbChars.ToString());
                        sbChars = new StringBuilder();
                        lastAttrName = null;
                        break;

                    case (int)ActionCode.SEND_CHARS:
                        handler.OnChars(sbChars.ToString());
                        sbChars = new StringBuilder();
                        break;

                    case (int)ActionCode.START_CDATA:
                        string cdata = "CDATA[";
                        isComment = false;
                        isDTD = false;

                        if (currCh == '-')
                        {
                            currCh = reader.Read();

                            if (currCh != '-') FatalErr("Invalid comment");

                            this.col++;
                            isComment = true;
                            twoCharBuff[0] = -1;
                            twoCharBuff[1] = -1;
                        }
                        else
                        {
                            if (currCh != '[')
                            {
                                isDTD = true;
                                bracketSwitch = 0;
                                break;
                            }

                            for (int i = 0; i < cdata.Length; i++)
                            {
                                if (reader.Read() != cdata[i])
                                {
                                    this.col += i + 1;
                                    break;
                                }
                            }
                            this.col += cdata.Length;
                            seenCData = true;
                        }
                        break;

                    case (int)ActionCode.END_CDATA:
                        int n = 0;
                        currCh = ']';

                        while (currCh == ']')
                        {
                            currCh = reader.Read();
                            n++;
                        }

                        if (currCh != '>')
                        {
                            for (int i = 0; i < n; i++) sbChars.Append(']');
                            sbChars.Append((char)currCh);
                            stateCode = 0x12;
                        }
                        else
                        {
                            for (int i = 0; i < n - 2; i++) sbChars.Append(']');
                            seenCData = false;
                        }

                        this.col += n;
                        break;

                    case (int)ActionCode.ERROR:
                        FatalErr(String.Format("Error {0}", stateCode));
                        break;

                    case (int)ActionCode.STATE_CHANGE:
                        break;

                    case (int)ActionCode.FLUSH_CHARS_STATE_CHANGE:
                        sbChars = new StringBuilder();
                        if (currCh != '<') goto case (int)ActionCode.ACC_CHARS_STATE_CHANGE;
                        break;

                    case (int)ActionCode.ACC_CHARS_STATE_CHANGE:
                        sbChars.Append((char)currCh);
                        break;

                    case (int)ActionCode.ACC_CDATA:
                        if (isComment)
                        {
                            if (currCh == '>'
                                && twoCharBuff[0] == '-'
                                && twoCharBuff[1] == '-')
                            {
                                isComment = false;
                                stateCode = 0;
                            }
                            else
                            {
                                twoCharBuff[0] = twoCharBuff[1];
                                twoCharBuff[1] = currCh;
                            }
                        }
                        else if (isDTD)
                        {
                            if (currCh == '<' || currCh == '>') bracketSwitch ^= 1;
                            if (currCh == '>' && bracketSwitch != 0)
                            {
                                isDTD = false;
                                stateCode = 0;
                            }
                        }
                        else
                        {
                            if (this.splitCData
                                && sbChars.Length > 0
                                && seenCData)
                            {
                                handler.OnChars(sbChars.ToString());
                                sbChars = new StringBuilder();
                            }
                            seenCData = false;
                            sbChars.Append((char)currCh);
                        }
                        break;

                    case (int)ActionCode.PROC_CHAR_REF:
                        currCh = reader.Read();
                        int cl = this.col + 1;
                        if (currCh == '#')
                        {    // character reference
                            int r = 10;
                            int chCode = 0;
                            int nDigits = 0;
                            currCh = reader.Read();
                            cl++;

                            if (currCh == 'x')
                            {
                                currCh = reader.Read();
                                cl++;
                                r = 16;
                            }

                            NumberStyles style = r == 16 ? NumberStyles.HexNumber : NumberStyles.Integer;

                            while (true)
                            {
                                int x = -1;
                                if (Char.IsNumber((char)currCh) || "abcdef".IndexOf(Char.ToLower((char)currCh)) != -1)
                                {
                                    try
                                    {
                                        x = Int32.Parse(new string((char)currCh, 1), style);
                                    }
                                    catch (FormatException) { x = -1; }
                                }
                                if (x == -1) break;
                                chCode *= r;
                                chCode += x;
                                nDigits++;
                                currCh = reader.Read();
                                cl++;
                            }

                            if (currCh == ';' && nDigits > 0)
                            {
                                sbChars.Append((char)chCode);
                            }
                            else
                            {
                                FatalErr("Bad char ref");
                            }
                        }
                        else
                        {
                            // entity reference
                            string entityRefChars = "aglmopqstu"; // amp | apos | quot | gt | lt
                            string entities = "&'\"><";

                            int pos = 0;
                            int entIdx = 0xF;
                            int pred = 0;
                            int predShift = 0;

                            int sbLen = sbChars.Length;

                            while (true)
                            {
                                if (pos != 0xF) pos = entityRefChars.IndexOf((char)currCh) & 0xF;
                                if (pos == 0xF) FatalErr(errors[7]);
                                sbChars.Append((char)currCh);

                                int path = "\uFF35\u3F8F\u4F8F\u0F5F\uFF78\uE1F4\u2299\uEEFF\uEEFF\uFF4F"[pos];
                                int lBr = (path >> 4) & 0xF;
                                int rBr = path & 0xF;
                                int lPred = path >> 12;
                                int rPred = (path >> 8) & 0xF;
                                currCh = reader.Read();
                                cl++;
                                pos = 0xF;
                                if (lBr != 0xF && currCh == entityRefChars[lBr])
                                {
                                    if (lPred < 0xE) entIdx = lPred;
                                    pred = lPred;
                                    predShift = 12; // left
                                }
                                else if (rBr != 0xF && currCh == entityRefChars[rBr])
                                {
                                    if (rPred < 0xE) entIdx = rPred;
                                    pred = rPred;
                                    predShift = 8; // right
                                }
                                else if (currCh == ';')
                                {
                                    if (entIdx != 0xF
                                        && predShift != 0
                                        && ((path >> predShift) & 0xF) == 0xE) break;
                                    continue; // pos == 0xF
                                }

                                pos = 0;

                            }

                            int l = cl - this.col - 1;

                            if ((l > 0 && l < 5)
                                && (StrEquals("amp", sbChars, sbLen, l)
                                || StrEquals("apos", sbChars, sbLen, l)
                                || StrEquals("quot", sbChars, sbLen, l)
                                || StrEquals("lt", sbChars, sbLen, l)
                                || StrEquals("gt", sbChars, sbLen, l))
                                )
                            {
                                sbChars.Length = sbLen;
                                sbChars.Append(entities[entIdx]);
                            }
                            else FatalErr(errors[7]);
                        }

                        this.col = cl;
                        break;

                    default:
                        FatalErr(String.Format("Unexpected action code - {0}.", actionCode));
                        break;
                }
            } // while (true)

            handler.OnEndParsing(this);

        } // Parse

    }

    /*
 * id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
 * 
 * KeyUsage ::= BIT STRING {
 *	digitalSignature        (0),
 * 	nonRepudiation          (1),
 * 	keyEncipherment         (2),
 * 	dataEncipherment        (3),
 * 	keyAgreement            (4),
 * 	keyCertSign             (5),
 * 	cRLSign                 (6),
 * 	encipherOnly            (7),
 * 	decipherOnly            (8) 
 * }
 */
    // note: because nothing is simple in ASN.1 bits are reversed
    [Flags]
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 enum KeyUsages
    {
        digitalSignature = 0x80,
        nonRepudiation = 0x40,
        keyEncipherment = 0x20,
        dataEncipherment = 0x10,
        keyAgreement = 0x08,
        keyCertSign = 0x04,
        cRLSign = 0x02,
        encipherOnly = 0x01,
        decipherOnly = 0x800,
        none = 0x0
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class KeyUsageExtension : X509Extension
    {

        private int kubits;

        public KeyUsageExtension(ASN1 asn1) : base(asn1) { }

        public KeyUsageExtension(X509Extension extension) : base(extension) { }

        public KeyUsageExtension()
            : base()
        {
            extnOid = "2.5.29.15";
        }

        protected override void Decode()
        {
            ASN1 bitString = new ASN1(extnValue.Value);
            if (bitString.Tag != 0x03)
                throw new ArgumentException("Invalid KeyUsage extension");
            int i = 1; // byte zero has the number of unused bits (ASN1's BITSTRING)
            while (i < bitString.Value.Length)
                kubits = (kubits << 8) + bitString.Value[i++];
        }

        protected override void Encode()
        {
            extnValue = new ASN1(0x04);

            ushort ku = (ushort)kubits;
            byte unused = 16;
            if (ku > 0)
            {
                // count the unused bits
                for (unused = 15; unused > 0; unused--)
                {
                    if ((ku & 0x8000) == 0x8000)
                        break;
                    ku <<= 1;
                }

                if (kubits > Byte.MaxValue)
                {
                    unused -= 8;
                    extnValue.Add(new ASN1(0x03, new byte[] { unused, (byte)kubits, (byte)(kubits >> 8) }));
                }
                else
                {
                    extnValue.Add(new ASN1(0x03, new byte[] { unused, (byte)kubits }));
                }
            }
            else
            {
                // note: a BITSTRING with a 0 length is invalid (in ASN.1), so would an
                // empty OCTETSTRING (at the parent level) so we're encoding a 0
                extnValue.Add(new ASN1(0x03, new byte[] { 7, 0 }));
            }
        }

        public KeyUsages KeyUsage
        {
            get { return (KeyUsages)kubits; }
            set { kubits = Convert.ToInt32(value, CultureInfo.InvariantCulture); }
        }

        public override string Name
        {
            get { return "Key Usage"; }
        }

        public bool Support(KeyUsages usage)
        {
            int x = Convert.ToInt32(usage, CultureInfo.InvariantCulture);
            return ((x & kubits) == x);
        }

        public override string ToString()
        {
            const string separator = " , ";
            StringBuilder sb = new StringBuilder();
            if (Support(KeyUsages.digitalSignature))
                sb.Append("Digital Signature");
            if (Support(KeyUsages.nonRepudiation))
            {
                if (sb.Length > 0)
                    sb.Append(separator);
                sb.Append("Non-Repudiation");
            }
            if (Support(KeyUsages.keyEncipherment))
            {
                if (sb.Length > 0)
                    sb.Append(separator);
                sb.Append("Key Encipherment");
            }
            if (Support(KeyUsages.dataEncipherment))
            {
                if (sb.Length > 0)
                    sb.Append(separator);
                sb.Append("Data Encipherment");
            }
            if (Support(KeyUsages.keyAgreement))
            {
                if (sb.Length > 0)
                    sb.Append(separator);
                sb.Append("Key Agreement");
            }
            if (Support(KeyUsages.keyCertSign))
            {
                if (sb.Length > 0)
                    sb.Append(separator);
                sb.Append("Certificate Signing");
            }
            if (Support(KeyUsages.cRLSign))
            {
                if (sb.Length > 0)
                    sb.Append(separator);
                sb.Append("CRL Signing");
            }
            if (Support(KeyUsages.encipherOnly))
            {
                if (sb.Length > 0)
                    sb.Append(separator);
                sb.Append("Encipher Only ");	// ???
            }
            if (Support(KeyUsages.decipherOnly))
            {
                if (sb.Length > 0)
                    sb.Append(separator);
                sb.Append("Decipher Only");	// ???
            }
            sb.Append("(");
            sb.Append(kubits.ToString("X2", CultureInfo.InvariantCulture));
            sb.Append(")");
            sb.Append(Environment.NewLine);
            return sb.ToString();
        }
    }

    /*
 * id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::=  { id-ce 14 }
 * 
 * SubjectKeyIdentifier ::= KeyIdentifier
 * 
 * KeyIdentifier ::= OCTET STRING
 */

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class SubjectKeyIdentifierExtension : X509Extension
    {

        private byte[] ski;

        public SubjectKeyIdentifierExtension()
            : base()
        {
            extnOid = "2.5.29.14";
        }

        public SubjectKeyIdentifierExtension(ASN1 asn1)
            : base(asn1)
        {
        }

        public SubjectKeyIdentifierExtension(X509Extension extension)
            : base(extension)
        {
        }

        protected override void Decode()
        {
            ASN1 sequence = new ASN1(extnValue.Value);
            if (sequence.Tag != 0x04)
                throw new ArgumentException("Invalid SubjectKeyIdentifier extension");
            ski = sequence.Value;
        }

        public override string Name
        {
            get { return "Subject Key Identifier"; }
        }

        public byte[] Identifier
        {
            get
            {
                if (ski == null)
                    return null;
                return (byte[])ski.Clone();
            }
        }

        public override string ToString()
        {
            if (ski == null)
                return null;

            StringBuilder sb = new StringBuilder();
            int x = 0;
            while (x < ski.Length)
            {
                sb.Append(ski[x].ToString("X2", CultureInfo.InvariantCulture));
                if (x % 2 == 1)
                    sb.Append(" ");
                x++;
            }
            return sb.ToString();
        }
    }

    // References:
    // 1.	RFC 3280: Internet X.509 Public Key Infrastructure, Section 4.2.1.10
    //	http://www.ietf.org/rfc/rfc3280.txt

    /* id-ce-basicConstraints OBJECT IDENTIFIER ::=  { id-ce 19 }
     * 
     * BasicConstraints ::= SEQUENCE {
     * 	cA                      BOOLEAN DEFAULT FALSE,
     * 	pathLenConstraint       INTEGER (0..MAX) OPTIONAL 
     * }
     */
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class BasicConstraintsExtension : X509Extension
    {

        public const int NoPathLengthConstraint = -1;

        private bool cA;
        private int pathLenConstraint;

        public BasicConstraintsExtension()
            : base()
        {
            extnOid = "2.5.29.19";
            pathLenConstraint = NoPathLengthConstraint;
        }

        public BasicConstraintsExtension(ASN1 asn1) : base(asn1) { }

        public BasicConstraintsExtension(X509Extension extension) : base(extension) { }

        protected override void Decode()
        {
            // default values
            cA = false;
            pathLenConstraint = NoPathLengthConstraint;

            ASN1 sequence = new ASN1(extnValue.Value);
            if (sequence.Tag != 0x30)
                throw new ArgumentException("Invalid BasicConstraints extension");
            int n = 0;
            ASN1 a = sequence[n++];
            if ((a != null) && (a.Tag == 0x01))
            {
                cA = (a.Value[0] == 0xFF);
                a = sequence[n++];
            }
            if ((a != null) && (a.Tag == 0x02))
                pathLenConstraint = ASN1Convert.ToInt32(a);
        }

        protected override void Encode()
        {
            ASN1 seq = new ASN1(0x30);
            if (cA)
                seq.Add(new ASN1(0x01, new byte[] { 0xFF }));
            // CAs MUST NOT include the pathLenConstraint field unless the cA boolean is asserted
            if (cA && (pathLenConstraint >= 0))
                seq.Add(ASN1Convert.FromInt32(pathLenConstraint));

            extnValue = new ASN1(0x04);
            extnValue.Add(seq);
        }

        public bool CertificateAuthority
        {
            get { return cA; }
            set { cA = value; }
        }

        public override string Name
        {
            get { return "Basic Constraints"; }
        }

        public int PathLenConstraint
        {
            get { return pathLenConstraint; }
            set
            {
                if (value < NoPathLengthConstraint)
                {
                    string msg = String.Format("PathLenConstraint must be positive or -1 for none ({0}).", value);
                    throw new ArgumentOutOfRangeException(msg);
                }
                pathLenConstraint = value;
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Subject Type=");
            sb.Append((cA) ? "CA" : "End Entity");
            sb.Append(Environment.NewLine);
            sb.Append("Path Length Constraint=");
            if (pathLenConstraint == NoPathLengthConstraint)
                sb.Append("None");
            else
                sb.Append(pathLenConstraint.ToString(CultureInfo.InvariantCulture));
            sb.Append(Environment.NewLine);
            return sb.ToString();
        }
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 enum UriPartial
    {
        Scheme = 0,
        Authority = 1,
        Path = 2,
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class Uri
    {

        // NOTES:
        // o  scheme excludes the scheme delimiter
        // o  port is -1 to indicate no port is defined
        // o  path is empty or starts with / when scheme delimiter == "://"
        // o  query is empty or starts with ? char, escaped.
        // o  fragment is empty or starts with # char, unescaped.
        // o  all class variables are in escaped format when they are escapable,
        //    except cachedToString.
        // o  UNC is supported, as starts with "\\" for windows,
        //    or "//" with unix.

        private bool isUnixFilePath = false;
        private string source;
        private string scheme = String.Empty;
        private string host = String.Empty;
        private int port = -1;
        private string path = String.Empty;
        private string query = String.Empty;
        private string fragment = String.Empty;
        private string userinfo = String.Empty;
        private bool isUnc = false;
        private bool isOpaquePart = false;

        private string[] segments;

        private bool userEscaped = false;
        private string cachedAbsoluteUri = null;
        private string cachedToString = null;
        private string cachedLocalPath = null;
        private int cachedHashCode = 0;
        private bool reduce = true;

        private static readonly string hexUpperChars = "0123456789ABCDEF";

        // Fields

        public static readonly string SchemeDelimiter = "://";
        public static readonly string UriSchemeFile = "file";
        public static readonly string UriSchemeFtp = "ftp";
        public static readonly string UriSchemeGopher = "gopher";
        public static readonly string UriSchemeHttp = "http";
        public static readonly string UriSchemeHttps = "https";
        public static readonly string UriSchemeMailto = "mailto";
        public static readonly string UriSchemeNews = "news";
        public static readonly string UriSchemeNntp = "nntp";

        // Constructors		

        public Uri(string uriString)
            : this(uriString, false)
        {
        }

        public Uri(string uriString, bool dontEscape)
        {
            userEscaped = dontEscape;
            source = uriString;
            Parse();
        }

        public Uri(string uriString, bool dontEscape, bool reduce)
        {
            userEscaped = dontEscape;
            source = uriString;
            this.reduce = reduce;
            Parse();
        }

        public Uri(Uri baseUri, string relativeUri)
            : this(baseUri, relativeUri, false)
        {
        }

        public Uri(Uri baseUri, string relativeUri, bool dontEscape)
        {
            if (baseUri == null)
                throw new NullReferenceException("baseUri");

            // See RFC 2396 Par 5.2 and Appendix C

            userEscaped = dontEscape;

            if (relativeUri == null)
                throw new NullReferenceException("relativeUri");

            // Check Windows UNC (for // it is scheme/host separator)
            if (relativeUri.StartsWith("\\\\"))
            {
                source = relativeUri;
                Parse();
                return;
            }

            int pos = relativeUri.IndexOf(':');
            if (pos != -1)
            {

                int pos2 = relativeUri.IndexOfAny(new char[] { '/', '\\', '?' });

                // pos2 < 0 ... e.g. mailto
                // pos2 > pos ... to block ':' in query part
                if (pos2 > pos || pos2 < 0)
                {
                    // equivalent to new Uri (relativeUri, dontEscape)
                    source = relativeUri;
                    Parse();

                    return;
                }
            }

            this.scheme = baseUri.scheme;
            this.host = baseUri.host;
            this.port = baseUri.port;
            this.userinfo = baseUri.userinfo;
            this.isUnc = baseUri.isUnc;
            this.isUnixFilePath = baseUri.isUnixFilePath;
            this.isOpaquePart = baseUri.isOpaquePart;

            if (relativeUri == String.Empty)
            {
                this.path = baseUri.path;
                this.query = baseUri.query;
                this.fragment = baseUri.fragment;
                return;
            }

            // 8 fragment
            // Note that in relative constructor, file URI cannot handle '#' as a filename character, but just regarded as a fragment identifier.
            pos = relativeUri.IndexOf('#');
            if (pos != -1)
            {
                fragment = relativeUri.Substring(pos);
                // fragment is not escaped.
                relativeUri = relativeUri.Substring(0, pos);
            }

            // 6 query
            pos = relativeUri.IndexOf('?');
            if (pos != -1)
            {
                query = relativeUri.Substring(pos);
                if (!userEscaped)
                    query = EscapeString(query);
                relativeUri = relativeUri.Substring(0, pos);
            }

            if (relativeUri.Length > 0 && relativeUri[0] == '/')
            {
                if (relativeUri.Length > 1 && relativeUri[1] == '/')
                {
                    source = scheme + ':' + relativeUri;
                    Parse();
                    return;
                }
                else
                {
                    path = relativeUri;
                    if (!userEscaped)
                        path = EscapeString(path);
                    return;
                }
            }

            // par 5.2 step 6 a)
            path = baseUri.path;
            if (relativeUri.Length > 0 || query.Length > 0)
            {
                pos = path.LastIndexOf('/');
                if (pos >= 0)
                    path = path.Substring(0, pos + 1);
            }

            if (relativeUri.Length == 0)
                return;

            // 6 b)
            path += relativeUri;

            // 6 c)
            int startIndex = 0;
            while (true)
            {
                pos = path.IndexOf("./", startIndex);
                if (pos == -1)
                    break;
                if (pos == 0)
                    path = path.Remove(0, 2);
                else if (path[pos - 1] != '.')
                    path = path.Remove(pos, 2);
                else
                    startIndex = pos + 1;
            }

            // 6 d)
            if (path.Length > 1 &&
                path[path.Length - 1] == '.' &&
                path[path.Length - 2] == '/')
                path = path.Remove(path.Length - 1, 1);

            // 6 e)
            startIndex = 0;
            while (true)
            {
                pos = path.IndexOf("/../", startIndex);
                if (pos == -1)
                    break;
                if (pos == 0)
                {
                    startIndex = 3;
                    continue;
                }
                int pos2 = path.LastIndexOf('/', pos - 1);
                if (pos2 == -1)
                {
                    startIndex = pos + 1;
                }
                else
                {
                    if (path.Substring(pos2 + 1, pos - pos2 - 1) != "..")
                        path = path.Remove(pos2 + 1, pos - pos2 + 3);
                    else
                        startIndex = pos + 1;
                }
            }

            // 6 f)
            if (path.Length > 3 && path.EndsWith("/.."))
            {
                pos = path.LastIndexOf('/', path.Length - 4);
                if (pos != -1)
                    if (path.Substring(pos + 1, path.Length - pos - 4) != "..")
                        path = path.Remove(pos + 1, path.Length - pos - 1);
            }

            if (!userEscaped)
                path = EscapeString(path);
        }

        // Properties

        public string AbsolutePath
        {
            get { return path; }
        }

        public string AbsoluteUri
        {
            get
            {
                if (cachedAbsoluteUri == null)
                {
                    cachedAbsoluteUri = GetLeftPart(UriPartial.Path) + query + fragment;
                }
                return cachedAbsoluteUri;
            }
        }

        public string Authority
        {
            get
            {
                return (GetDefaultPort(scheme) == port)
                     ? host : host + ":" + port;
            }
        }

        public string Fragment
        {
            get { return fragment; }
        }

        public string Host
        {
            get { return host; }
        }

        /*		public UriHostNameType HostNameType { 
                    get {
                        UriHostNameType ret = CheckHostName (host);
                        if (ret != UriHostNameType.Unknown)
                            return ret;

                        // looks it always returns Basic...
                        return UriHostNameType.Basic; //.Unknown;
                    } 
                }*/

        public bool IsDefaultPort
        {
            get { return GetDefaultPort(scheme) == port; }
        }

        public bool IsFile
        {
            get { return (scheme == UriSchemeFile); }
        }

        public bool IsLoopback
        {
            get
            {
                if (host == String.Empty)
                    return false;

                if (host == "loopback" || host == "localhost")
                    return true;
                /*					
                                try {
                                    if (IPAddress.Loopback.Equals (IPAddress.Parse (host)))
                                        return true;
                                } catch (FormatException) {}

                                try {
                                    return IPv6Address.IsLoopback (IPv6Address.Parse (host));
                                } catch (FormatException) {}
                */
                return false;
            }
        }

        public bool IsUnc
        {
            // rule: This should be true only if
            //   - uri string starts from "\\", or
            //   - uri string starts from "//" (Samba way)
            get { return isUnc; }
        }

        public string LocalPath
        {
            get
            {
                if (cachedLocalPath != null)
                    return cachedLocalPath;
                if (!IsFile)
                    return AbsolutePath;

                bool windows = (path.Length > 3 && path[1] == ':' &&
                        (path[2] == '\\' || path[2] == '/'));

                if (!IsUnc)
                {
                    string p = Unescape(path);
                    if (System.IO.Path.DirectorySeparatorChar == '\\' || windows)
                        cachedLocalPath = p.Replace('/', '\\');
                    else
                        cachedLocalPath = p;
                }
                else
                {
                    // support *nix and W32 styles
                    if (path.Length > 1 && path[1] == ':')
                        cachedLocalPath = Unescape(path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar));

                    // LAMESPEC: ok, now we cannot determine
                    // if such URI like "file://foo/bar" is
                    // Windows UNC or unix file path, so
                    // they should be handled differently.
                    else if (System.IO.Path.DirectorySeparatorChar == '\\')
                        cachedLocalPath = "\\\\" + Unescape(host + path.Replace('/', '\\'));
                    else
                        cachedLocalPath = Unescape(path);
                }
                if (cachedLocalPath == String.Empty)
                    cachedLocalPath = Path.DirectorySeparatorChar.ToString();
                return cachedLocalPath;
            }
        }

        public string PathAndQuery
        {
            get { return path + query; }
        }

        public int Port
        {
            get { return port; }
        }

        public string Query
        {
            get { return query; }
        }

        public string Scheme
        {
            get { return scheme; }
        }

        public string[] Segments
        {
            get
            {
                if (segments != null)
                    return segments;

                if (path == "")
                {
                    segments = new string[0];
                    return segments;
                }

                string[] parts = path.Split('/');
                segments = parts;
                bool endSlash = path.EndsWith("/");
                if (parts.Length > 0 && endSlash)
                {
                    string[] newParts = new string[parts.Length - 1];
                    Array.Copy(parts, 0, newParts, 0, parts.Length - 1);
                    parts = newParts;
                }

                int i = 0;
                if (IsFile && path.Length > 1 && path[1] == ':')
                {
                    string[] newParts = new string[parts.Length + 1];
                    Array.Copy(parts, 1, newParts, 2, parts.Length - 1);
                    parts = newParts;
                    parts[0] = path.Substring(0, 2);
                    parts[1] = "";
                    i++;
                }

                int end = parts.Length;
                for (; i < end; i++)
                    if (i != end - 1 || endSlash)
                        parts[i] += '/';

                segments = parts;
                return segments;
            }
        }

        public bool UserEscaped
        {
            get { return userEscaped; }
        }

        public string UserInfo
        {
            get { return userinfo; }
        }


        // Methods		

        /*		public static UriHostNameType CheckHostName (string name) 
                {
                    if (name == null || name.Length == 0)
                        return UriHostNameType.Unknown;

                    if (IsIPv4Address (name)) 
                        return UriHostNameType.IPv4;
				
                    if (IsDomainAddress (name))
                        return UriHostNameType.Dns;				
				
                    try {
                        IPv6Address.Parse (name);
                        return UriHostNameType.IPv6;
                    } catch (FormatException) {}
			
                    return UriHostNameType.Unknown;
                }*/

        internal static bool IsIPv4Address(string name)
        {
            string[] captures = name.Split(new char[] { '.' });
            if (captures.Length != 4)
                return false;
            for (int i = 0; i < 4; i++)
            {
                try
                {
                    int d = Int32.Parse(captures[i], CultureInfo.InvariantCulture);
                    if (d < 0 || d > 255)
                        return false;
                }
                catch (Exception)
                {
                    return false;
                }
            }
            return true;
        }

        internal static bool IsDomainAddress(string name)
        {
            int len = name.Length;

            if (name[len - 1] == '.')
                return false;

            int count = 0;
            for (int i = 0; i < len; i++)
            {
                char c = name[i];
                if (count == 0)
                {
                    if (!Char.IsLetterOrDigit(c))
                        return false;
                }
                else if (c == '.')
                {
                    count = 0;
                }
                else if (!Char.IsLetterOrDigit(c) && c != '-' && c != '_')
                {
                    return false;
                }
                if (++count == 64)
                    return false;
            }

            return true;
        }

        /*		[MonoTODO ("Find out what this should do")]
                protected virtual void Canonicalize ()
                {
                }*/

        public static bool CheckSchemeName(string schemeName)
        {
            if (schemeName == null || schemeName.Length == 0)
                return false;

            if (!Char.IsLetter(schemeName[0]))
                return false;

            int len = schemeName.Length;
            for (int i = 1; i < len; i++)
            {
                char c = schemeName[i];
                if (!Char.IsLetterOrDigit(c) && c != '.' && c != '+' && c != '-')
                    return false;
            }

            return true;
        }

        /*		[MonoTODO ("Find out what this should do")]
                protected virtual void CheckSecurity ()
                {
                }*/

        public override bool Equals(object comparant)
        {
            if (comparant == null)
                return false;

            Uri uri = comparant as Uri;
            if (uri == null)
            {
                string s = comparant as String;
                if (s == null)
                    return false;
                uri = new Uri(s);
            }

            CultureInfo inv = CultureInfo.InvariantCulture;
            return ((this.scheme.ToLower(inv) == uri.scheme.ToLower(inv)) &&
                (this.userinfo.ToLower(inv) == uri.userinfo.ToLower(inv)) &&
                (this.host.ToLower(inv) == uri.host.ToLower(inv)) &&
                (this.port == uri.port) &&
                (this.path == uri.path) &&
                (this.query.ToLower(inv) == uri.query.ToLower(inv)));
        }

        public override int GetHashCode()
        {
            if (cachedHashCode == 0)
                cachedHashCode = scheme.GetHashCode()
                               + userinfo.GetHashCode()
                           + host.GetHashCode()
                           + port
                           + path.GetHashCode()
                           + query.GetHashCode();
            return cachedHashCode;
        }

        public string GetLeftPart(UriPartial part)
        {
            int defaultPort;
            switch (part)
            {
                case UriPartial.Scheme:
                    return scheme + GetOpaqueWiseSchemeDelimiter();
                case UriPartial.Authority:
                    if (host == String.Empty ||
                        scheme == Uri.UriSchemeMailto ||
                        scheme == Uri.UriSchemeNews)
                        return String.Empty;

                    StringBuilder s = new StringBuilder();
                    s.Append(scheme);
                    s.Append(GetOpaqueWiseSchemeDelimiter());
                    if (path.Length > 1 && path[1] == ':' && (Uri.UriSchemeFile == scheme))
                        s.Append('/');  // win32 file
                    if (userinfo.Length > 0)
                        s.Append(userinfo).Append('@');
                    s.Append(host);
                    defaultPort = GetDefaultPort(scheme);
                    if ((port != -1) && (port != defaultPort))
                        s.Append(':').Append(port);
                    return s.ToString();
                case UriPartial.Path:
                    StringBuilder sb = new StringBuilder();
                    sb.Append(scheme);
                    sb.Append(GetOpaqueWiseSchemeDelimiter());
                    if (path.Length > 1 && path[1] == ':' && (Uri.UriSchemeFile == scheme))
                        sb.Append('/');  // win32 file
                    if (userinfo.Length > 0)
                        sb.Append(userinfo).Append('@');
                    sb.Append(host);
                    defaultPort = GetDefaultPort(scheme);
                    if ((port != -1) && (port != defaultPort))
                        sb.Append(':').Append(port);
                    sb.Append(path);
                    return sb.ToString();
            }
            return null;
        }

        public static int FromHex(char digit)
        {
            if ('0' <= digit && digit <= '9')
            {
                return (int)(digit - '0');
            }

            if ('a' <= digit && digit <= 'f')
                return (int)(digit - 'a' + 10);

            if ('A' <= digit && digit <= 'F')
                return (int)(digit - 'A' + 10);

            throw new ArgumentException("digit");
        }

        public static string HexEscape(char character)
        {
            if (character > 255)
            {
                throw new ArgumentOutOfRangeException("character");
            }

            return "%" + hexUpperChars[((character & 0xf0) >> 4)]
                       + hexUpperChars[((character & 0x0f))];
        }

        public static char HexUnescape(string pattern, ref int index)
        {
            if (pattern == null)
                throw new ArgumentException("pattern");

            if (index < 0 || index >= pattern.Length)
                throw new ArgumentOutOfRangeException("index");

            int stage = 0;
            int c = 0;
            do
            {
                if (((index + 3) > pattern.Length) ||
                    (pattern[index] != '%') ||
                    !IsHexDigit(pattern[index + 1]) ||
                    !IsHexDigit(pattern[index + 2]))
                {
                    if (stage == 0)
                        return pattern[index++];
                    break;
                }

                index++;
                int msb = FromHex(pattern[index++]);
                int lsb = FromHex(pattern[index++]);
                int b = (msb << 4) + lsb;

                if (stage == 0)
                {
                    if (b < 0xc0)
                        return (char)b;
                    else if (b < 0xE0)
                    {
                        c = b - 0xc0;
                        stage = 2;
                    }
                    else if (b < 0xF0)
                    {
                        c = b - 0xe0;
                        stage = 3;
                    }
                    else if (b < 0xF8)
                    {
                        c = b - 0xf0;
                        stage = 4;
                    }
                    else if (b < 0xFB)
                    {
                        c = b - 0xf8;
                        stage = 5;
                    }
                    else if (b < 0xFE)
                    {
                        c = b - 0xfc;
                        stage = 6;
                    }
                    c <<= (stage - 1) * 6;
                }
                else
                    c += (b - 0x80) << ((stage - 1) * 6);
                //Console.WriteLine ("stage {0}: {5:X04} <-- {1:X02}|{2:X01},{3:X01} {4}", new object [] {stage, b, msb, lsb, pattern.Substring (index), c});
                stage--;
            } while (stage > 0);

            return (char)c;
        }

        public static bool IsHexDigit(char digit)
        {
            return (('0' <= digit && digit <= '9') ||
                    ('a' <= digit && digit <= 'f') ||
                    ('A' <= digit && digit <= 'F'));
        }

        public static bool IsHexEncoding(string pattern, int index)
        {
            if ((index + 3) > pattern.Length)
                return false;

            return ((pattern[index++] == '%') &&
                    IsHexDigit(pattern[index++]) &&
                    IsHexDigit(pattern[index]));
        }

        public string MakeRelative(Uri toUri)
        {
            if ((this.Scheme != toUri.Scheme) ||
                (this.Authority != toUri.Authority))
                return toUri.ToString();

            if (this.path == toUri.path)
                return String.Empty;

            string[] segments = this.Segments;
            string[] segments2 = toUri.Segments;

            int k = 0;
            int max = System.Math.Min(segments.Length, segments2.Length);
            for (; k < max; k++)
                if (segments[k] != segments2[k])
                    break;

            string result = String.Empty;
            for (int i = k + 1; i < segments.Length; i++)
                result += "../";
            for (int i = k; i < segments2.Length; i++)
                result += segments2[i];

            return result;
        }

        public override string ToString()
        {
            if (cachedToString != null)
                return cachedToString;
            string q = query.StartsWith("?") ? '?' + Unescape(query.Substring(1)) : Unescape(query);
            cachedToString = Unescape(GetLeftPart(UriPartial.Path), true) + q + fragment;
            return cachedToString;
        }

        /*		void ISerializable.GetObjectData (SerializationInfo info, 
                              StreamingContext context)
                {
                    info.AddValue ("AbsoluteUri", this.AbsoluteUri);
                }*/


        // Internal Methods		

        protected void Escape()
        {
            path = EscapeString(path);
        }

        protected static string EscapeString(string str)
        {
            return EscapeString(str, false, true, true);
        }

        internal static string EscapeString(string str, bool escapeReserved, bool escapeHex, bool escapeBrackets)
        {
            if (str == null)
                return String.Empty;

            byte[] data = Encoding.UTF8.GetBytes(str.ToCharArray());
            StringBuilder s = new StringBuilder();
            int len = data.Length;
            for (int i = 0; i < len; i++)
            {
                char c = (char)data[i];
                // reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
                // mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
                // control     = <US-ASCII coded characters 00-1F and 7F hexadecimal>
                // space       = <US-ASCII coded character 20 hexadecimal>
                // delims      = "<" | ">" | "#" | "%" | <">
                // unwise      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"

                // check for escape code already placed in str, 
                // i.e. for encoding that follows the pattern 
                // "%hexhex" in a string, where "hex" is a digit from 0-9 
                // or a letter from A-F (case-insensitive).
                if ('%'.Equals(c) && IsHexEncoding(str, i))
                {
                    // if ,yes , copy it as is
                    s.Append(c);
                    s.Append(str[++i]);
                    s.Append(str[++i]);
                    continue;
                }

                if ((c <= 0x20) || (c >= 0x7f) ||
                    ("<>%\"{}|\\^`".IndexOf(c) != -1) ||
                    (escapeHex && (c == '#')) ||
                    (escapeBrackets && (c == '[' || c == ']')) ||
                    (escapeReserved && (";/?:@&=+$,".IndexOf(c) != -1)))
                {
                    s.Append(HexEscape(c));
                    continue;
                }


                s.Append(c);
            }

            return s.ToString();
        }

        // This method is called from .ctor(). When overriden, we can
        // avoid the "absolute uri" constraints of the .ctor() by
        // overriding with custom code.
        protected void Parse()
        {
            Parse(source);

            if (userEscaped)
                return;

            host = EscapeString(host, false, true, false);
            path = EscapeString(path);
        }

        protected string Unescape(string str)
        {
            return Unescape(str, false);
        }

        internal string Unescape(string str, bool excludeSharp)
        {
            if (str == null)
                return String.Empty;
            StringBuilder s = new StringBuilder();
            int len = str.Length;
            for (int i = 0; i < len; i++)
            {
                char c = str[i];
                if (c == '%')
                {
                    char x = HexUnescape(str, ref i);
                    if (excludeSharp && x == '#')
                        s.Append("%23");
                    else
                        s.Append(x);
                    i--;
                }
                else
                    s.Append(c);
            }
            return s.ToString();
        }


        // Private Methods

        private void ParseAsWindowsUNC(string uriString)
        {
            scheme = UriSchemeFile;
            port = -1;
            fragment = String.Empty;
            query = String.Empty;
            isUnc = true;

            uriString = uriString.TrimStart(new char[] { '\\' });
            int pos = uriString.IndexOf('\\');
            if (pos > 0)
            {
                path = uriString.Substring(pos);
                host = uriString.Substring(0, pos);
            }
            else
            { // "\\\\server"
                host = uriString;
                path = String.Empty;
            }
            path = path.Replace("\\", "/");
        }

        private void ParseAsWindowsAbsoluteFilePath(string uriString)
        {
            if (uriString.Length > 2 && uriString[2] != '\\'
                    && uriString[2] != '/')
                throw new FormatException("Relative file path is not allowed.");
            scheme = UriSchemeFile;
            host = String.Empty;
            port = -1;
            path = uriString.Replace("\\", "/");
            fragment = String.Empty;
            query = String.Empty;
        }

        private void ParseAsUnixAbsoluteFilePath(string uriString)
        {
            isUnixFilePath = true;
            scheme = UriSchemeFile;
            port = -1;
            fragment = String.Empty;
            query = String.Empty;
            host = String.Empty;
            path = null;

            if (uriString.StartsWith("//"))
            {
                uriString = uriString.TrimStart(new char[] { '/' });
                // Now we don't regard //foo/bar as "foo" host.
                /* 
                int pos = uriString.IndexOf ('/');
                if (pos > 0) {
                    path = '/' + uriString.Substring (pos + 1);
                    host = uriString.Substring (0, pos);
                } else { // "///server"
                    host = uriString;
                    path = String.Empty;
                }
                */
                path = '/' + uriString;
            }
            if (path == null)
                path = uriString;
        }

        // this parse method is as relaxed as possible about the format
        // it will hardly ever throw a UriFormatException
        private void Parse(string uriString)
        {
            //
            // From RFC 2396 :
            //
            //      ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
            //       12            3  4          5       6  7        8 9
            //			

            if (uriString == null)
                throw new ArgumentNullException("uriString");

            int len = uriString.Length;
            if (len <= 1)
                throw new FormatException();

            int pos = 0;

            // 1, 2
            // Identify Windows path, unix path, or standard URI.
            pos = uriString.IndexOf(':');
            if (pos < 0)
            {
                // It must be Unix file path or Windows UNC
                if (uriString[0] == '/')
                    ParseAsUnixAbsoluteFilePath(uriString);
                else if (uriString.StartsWith("\\\\"))
                    ParseAsWindowsUNC(uriString);
                else
                    throw new FormatException("URI scheme was not recognized, nor input string is not recognized as an absolute file path.");
                return;
            }
            else if (pos == 1)
            {
                if (!Char.IsLetter(uriString[0]))
                    throw new FormatException("URI scheme must start with alphabet character.");
                // This means 'a:' == windows full path.
                ParseAsWindowsAbsoluteFilePath(uriString);
                return;
            }

            // scheme
            scheme = uriString.Substring(0, pos).ToLower(CultureInfo.InvariantCulture);
            // Check scheme name characters as specified in RFC2396.
            if (!Char.IsLetter(scheme[0]))
                throw new FormatException("URI scheme must start with alphabet character.");
            for (int i = 1; i < scheme.Length; i++)
            {
                if (!Char.IsLetterOrDigit(scheme, i))
                {
                    switch (scheme[i])
                    {
                        case '+':
                        case '-':
                        case '.':
                            break;
                        default:
                            throw new FormatException("URI scheme must consist of one of alphabet, digits, '+', '-' or '.' character.");
                    }
                }
            }
            uriString = uriString.Substring(pos + 1);

            // 8 fragment
            pos = uriString.IndexOf('#');
            if (!IsUnc && pos != -1)
            {
                fragment = uriString.Substring(pos);
                uriString = uriString.Substring(0, pos);
            }

            // 6 query
            pos = uriString.IndexOf('?');
            if (pos != -1)
            {
                query = uriString.Substring(pos);
                uriString = uriString.Substring(0, pos);
                if (!userEscaped)
                    query = EscapeString(query);
            }

            // 3
            bool unixAbsPath = scheme == UriSchemeFile && uriString.StartsWith("///");
            if (uriString.StartsWith("//"))
            {
                if (uriString.StartsWith("////"))
                    unixAbsPath = false;
                uriString = uriString.TrimStart(new char[] { '/' });
                if (uriString.Length > 1 && uriString[1] == ':')
                    unixAbsPath = false;
            }
            else if (!IsPredefinedScheme(scheme))
            {
                path = uriString;
                isOpaquePart = true;
                return;
            }

            // 5 path
            pos = uriString.IndexOfAny(new char[] { '/' });
            if (unixAbsPath)
                pos = -1;
            if (pos == -1)
            {
                if ((scheme != Uri.UriSchemeMailto) &&
                    (scheme != Uri.UriSchemeNews) &&
                    (scheme != Uri.UriSchemeFile))
                    path = "/";
            }
            else
            {
                path = uriString.Substring(pos);
                uriString = uriString.Substring(0, pos);
            }

            // 4.a user info
            pos = uriString.IndexOf("@");
            if (pos != -1)
            {
                userinfo = uriString.Substring(0, pos);
                uriString = uriString.Remove(0, pos + 1);
            }

            // 4.b port
            port = -1;
            pos = uriString.LastIndexOf(":");
            if (unixAbsPath)
                pos = -1;
            if (pos != -1 && pos != (uriString.Length - 1))
            {
                string portStr = uriString.Remove(0, pos + 1);
                if (portStr.Length > 1 && portStr[portStr.Length - 1] != ']')
                {
                    try
                    {
                        port = (int)UInt32.Parse(portStr, CultureInfo.InvariantCulture);
                        uriString = uriString.Substring(0, pos);
                    }
                    catch (Exception)
                    {
                        throw new FormatException("Invalid URI: invalid port number");
                    }
                }
            }
            if (port == -1)
            {
                port = GetDefaultPort(scheme);
            }

            // 4 authority
            host = uriString;
            /*			if (host.Length > 1 && host [0] == '[' && host [host.Length - 1] == ']') {
                            try {
                                host = "[" + IPv6Address.Parse (host).ToString () + "]";
                            } catch (Exception) {
                                throw new FormatException ("Invalid URI: The hostname could not be parsed");
                            }
                        }*/

            if (unixAbsPath)
            {
                path = '/' + uriString;
                host = String.Empty;
            }
            else if (host.Length == 2 && host[1] == ':')
            {
                // windows filepath
                path = host + path;
                host = String.Empty;
            }
            else if (isUnixFilePath)
            {
                uriString = "//" + uriString;
                host = String.Empty;
            }
            else if (host.Length == 0)
            {
                throw new FormatException("Invalid URI: The hostname could not be parsed");
            }
            else if (scheme == UriSchemeFile)
            {
                isUnc = true;
            }

            if ((scheme != Uri.UriSchemeMailto) &&
                    (scheme != Uri.UriSchemeNews) &&
                    (scheme != Uri.UriSchemeFile))

                if (reduce)
                    path = Reduce(path);
        }

        private static string Reduce(string path)
        {
            path = path.Replace('\\', '/');
            string[] parts = path.Split('/');
            ArrayList result = new ArrayList();

            int end = parts.Length;
            for (int i = 0; i < end; i++)
            {
                string current = parts[i];
                if (current == "" || current == ".")
                    continue;

                if (current == "..")
                {
                    if (result.Count == 0)
                    {
                        if (i == 1) // see bug 52599
                            continue;
                        throw new Exception("Invalid path.");
                    }

                    result.RemoveAt(result.Count - 1);
                    continue;
                }

                result.Add(current);
            }

            if (result.Count == 0)
                return "/";

            result.Insert(0, "");

            string res = String.Join("/", (string[])result.ToArray(typeof(string)));
            if (path.EndsWith("/"))
                res += '/';

            return res;
        }

        private struct UriScheme
        {
            public string scheme;
            public string delimiter;
            public int defaultPort;

            public UriScheme(string s, string d, int p)
            {
                scheme = s;
                delimiter = d;
                defaultPort = p;
            }
        };

        static UriScheme[] schemes = new UriScheme[] {
			new UriScheme (UriSchemeHttp, SchemeDelimiter, 80),
			new UriScheme (UriSchemeHttps, SchemeDelimiter, 443),
			new UriScheme (UriSchemeFtp, SchemeDelimiter, 21),
			new UriScheme (UriSchemeFile, SchemeDelimiter, -1),
			new UriScheme (UriSchemeMailto, ":", 25),
			new UriScheme (UriSchemeNews, ":", -1),
			new UriScheme (UriSchemeNntp, SchemeDelimiter, 119),
			new UriScheme (UriSchemeGopher, SchemeDelimiter, 70),
		};

        internal static string GetSchemeDelimiter(string scheme)
        {
            for (int i = 0; i < schemes.Length; i++)
                if (schemes[i].scheme == scheme)
                    return schemes[i].delimiter;
            return Uri.SchemeDelimiter;
        }

        internal static int GetDefaultPort(string scheme)
        {
            for (int i = 0; i < schemes.Length; i++)
                if (schemes[i].scheme == scheme)
                    return schemes[i].defaultPort;
            return -1;
        }

        private string GetOpaqueWiseSchemeDelimiter()
        {
            if (isOpaquePart)
                return ":";
            else
                return GetSchemeDelimiter(scheme);
        }

        protected bool IsBadFileSystemCharacter(char ch)
        {
            // It does not always overlap with InvalidPathChars.
            int chInt = (int)ch;
            if (chInt < 32 || (chInt < 64 && chInt > 57))
                return true;
            switch (chInt)
            {
                case 0:
                case 34: // "
                case 38: // &
                case 42: // *
                case 44: // ,
                case 47: // /
                case 92: // \
                case 94: // ^
                case 124: // |
                    return true;
            }

            return false;
        }


        protected static bool IsExcludedCharacter(char ch)
        {
            if (ch <= 32 || ch >= 127)
                return true;

            if (ch == '"' || ch == '#' || ch == '%' || ch == '<' ||
                ch == '>' || ch == '[' || ch == '\\' || ch == ']' ||
                ch == '^' || ch == '`' || ch == '{' || ch == '|' ||
                ch == '}')
                return true;
            return false;
        }

        private static bool IsPredefinedScheme(string scheme)
        {
            switch (scheme)
            {
                case "http":
                case "https":
                case "file":
                case "ftp":
                case "nntp":
                case "gopher":
                case "mailto":
                case "news":
                    return true;
                default:
                    return false;
            }
        }

        protected bool IsReservedCharacter(char ch)
        {
            if (ch == '$' || ch == '&' || ch == '+' || ch == ',' ||
                ch == '/' || ch == ':' || ch == ';' || ch == '=' ||
                ch == '@')
                return true;
            return false;
        }
    }


    /* RUNTIME
 *				yes
 *	in_gac ---------------------------------\
 *		|				|
 *		| no				\/
 *		|			return true
 * CLASS LIBRARY|
 *		|
 * 		|
 *		|				
 *	bool StrongNameManager.MustVerify
 *		|
 *		|
 *		\/		not found	
 *		Token --------------------------\
 *		|				|
 *		| present ?			|
 *		|				|
 *		\/		not found	|
 *	Assembly Name --------------------------|
 *		|				|
 *		| present ?			|
 *		| or "*"			|
 *		\/		not found	|
 *		User ---------------------------|
 *		|				|
 *		| present ?			|
 *		| or "*"			|
 *		\/				\/
 *	return false			return true
 *	SKIP VERIFICATION		VERIFY ASSEMBLY
 */

    internal class StrongNameManager
    {

        private class Element
        {
            internal Hashtable assemblies;

            public Element()
            {
                assemblies = new Hashtable();
            }

            public Element(string assembly, string users)
                : this()
            {
                assemblies.Add(assembly, users);
            }

            public string GetUsers(string assembly)
            {
                return (string)assemblies[assembly];
            }
        }

        static private Hashtable mappings;
        static private Hashtable tokens;

        static StrongNameManager()
        {
        }

        // note: more than one configuration file can be loaded at the 
        // same time (e.g. user specific and machine specific config).
        static public void LoadConfig(string filename)
        {
            if (File.Exists(filename))
            {
                SecurityParser sp = new SecurityParser();
                using (StreamReader sr = new StreamReader(filename))
                {
                    string xml = sr.ReadToEnd();
                    sp.LoadXml(xml);
                }
                SecurityElement root = sp.ToXml();
                if ((root != null) && (root.Tag == "configuration"))
                {
                    SecurityElement strongnames = root.SearchForChildByTag("strongNames");
                    if ((strongnames != null) && (strongnames.Children.Count > 0))
                    {
                        SecurityElement mapping = strongnames.SearchForChildByTag("pubTokenMapping");
                        if ((mapping != null) && (mapping.Children.Count > 0))
                        {
                            LoadMapping(mapping);
                        }

                        SecurityElement settings = strongnames.SearchForChildByTag("verificationSettings");
                        if ((settings != null) && (settings.Children.Count > 0))
                        {
                            LoadVerificationSettings(settings);
                        }
                    }
                }
            }
        }

        static private void LoadMapping(SecurityElement mapping)
        {
            if (mappings == null)
            {
                mappings = new Hashtable();
            }

            lock (mappings.SyncRoot)
            {
                foreach (SecurityElement item in mapping.Children)
                {
                    if (item.Tag != "map")
                        continue;

                    string token = item.Attribute("Token");
                    if ((token == null) || (token.Length != 16))
                        continue; // invalid entry
                    token = token.ToUpper(CultureInfo.InvariantCulture);

                    string publicKey = item.Attribute("PublicKey");
                    if (publicKey == null)
                        continue; // invalid entry

                    // watch for duplicate entries
                    if (mappings[token] == null)
                    {
                        mappings.Add(token, publicKey);
                    }
                    else
                    {
                        // replace existing mapping
                        mappings[token] = publicKey;
                    }
                }
            }
        }

        static private void LoadVerificationSettings(SecurityElement settings)
        {
            if (tokens == null)
            {
                tokens = new Hashtable();
            }

            lock (tokens.SyncRoot)
            {
                foreach (SecurityElement item in settings.Children)
                {
                    if (item.Tag != "skip")
                        continue;

                    string token = item.Attribute("Token");
                    if (token == null)
                        continue;	// bad entry
                    token = token.ToUpper(CultureInfo.InvariantCulture);

                    string assembly = item.Attribute("Assembly");
                    if (assembly == null)
                        assembly = "*";

                    string users = item.Attribute("Users");
                    if (users == null)
                        users = "*";

                    Element el = (Element)tokens[token];
                    if (el == null)
                    {
                        // new token
                        el = new Element(assembly, users);
                        tokens.Add(token, el);
                        continue;
                    }

                    // existing token
                    string a = (string)el.assemblies[assembly];
                    if (a == null)
                    {
                        // new assembly
                        el.assemblies.Add(assembly, users);
                        continue;
                    }

                    // existing assembly
                    if (users == "*")
                    {
                        // all users (drop current users)
                        el.assemblies[assembly] = "*";
                        continue;
                    }

                    // new users, add to existing
                    string existing = (string)el.assemblies[assembly];
                    string newusers = String.Concat(existing, ",", users);
                    el.assemblies[assembly] = newusers;
                }
            }
        }

        static public byte[] GetMappedPublicKey(byte[] token)
        {
            if ((mappings == null) || (token == null))
                return null;

            string t = CryptoConvert.ToHex(token);
            string pk = (string)mappings[t];
            if (pk == null)
                return null;

            return CryptoConvert.FromHex(pk);
        }

        // it is possible to skip verification for assemblies 
        // or a strongname public key using the "sn" tool.
        // note: only the runtime checks if the assembly is loaded 
        // from the GAC to skip verification
        static public bool MustVerify(System.Reflection.AssemblyName an)
        {
            if ((an == null) || (tokens == null))
                return true;

            string token = CryptoConvert.ToHex(an.GetPublicKeyToken());
            Element el = (Element)tokens[token];
            if (el != null)
            {
                // look for this specific assembly first
                string users = el.GetUsers(an.Name);
                if (users == null)
                {
                    // nothing for the specific assembly
                    // so look for "*" assembly
                    users = el.GetUsers("*");
                }

                if (users != null)
                {
                    // applicable to any user ?
                    if (users == "*")
                        return false;
                    // applicable to the current user ?
                    return (users.IndexOf(Environment.UserName) < 0);
                }
            }

            // we must check verify the strongname on the assembly
            return true;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Public Key Token\tAssemblies\t\tUsers");
            sb.Append(Environment.NewLine);
            foreach (DictionaryEntry token in tokens)
            {
                sb.Append((string)token.Key);
                Element t = (Element)token.Value;
                bool first = true;
                foreach (DictionaryEntry assembly in t.assemblies)
                {
                    if (first)
                    {
                        sb.Append("\t");
                        first = false;
                    }
                    else
                    {
                        sb.Append("\t\t\t");
                    }
                    sb.Append((string)assembly.Key);
                    sb.Append("\t");
                    string users = (string)assembly.Value;
                    if (users == "*")
                        users = "All users";
                    sb.Append(users);
                    sb.Append(Environment.NewLine);
                }
            }
            return sb.ToString();
        }
    }


#if INSIDE_CORLIB
	internal
#else
    public
#endif
 sealed class StrongName
    {

        internal class StrongNameSignature
        {
            private byte[] hash;
            private byte[] signature;
            private UInt32 signaturePosition;
            private UInt32 signatureLength;
            private UInt32 metadataPosition;
            private UInt32 metadataLength;
            private byte cliFlag;
            private UInt32 cliFlagPosition;

            public byte[] Hash
            {
                get { return hash; }
                set { hash = value; }
            }

            public byte[] Signature
            {
                get { return signature; }
                set { signature = value; }
            }

            public UInt32 MetadataPosition
            {
                get { return metadataPosition; }
                set { metadataPosition = value; }
            }

            public UInt32 MetadataLength
            {
                get { return metadataLength; }
                set { metadataLength = value; }
            }

            public UInt32 SignaturePosition
            {
                get { return signaturePosition; }
                set { signaturePosition = value; }
            }

            public UInt32 SignatureLength
            {
                get { return signatureLength; }
                set { signatureLength = value; }
            }

            // delay signed -> flag = 0x01
            // strongsigned -> flag = 0x09
            public byte CliFlag
            {
                get { return cliFlag; }
                set { cliFlag = value; }
            }

            public UInt32 CliFlagPosition
            {
                get { return cliFlagPosition; }
                set { cliFlagPosition = value; }
            }
        }

        internal enum StrongNameOptions
        {
            Metadata,
            Signature
        }

        private RSA rsa;
        private byte[] publicKey;
        private byte[] keyToken;
        private string tokenAlgorithm;

        public StrongName()
        {
        }

        public StrongName(int keySize)
        {
            rsa = new RSAManaged(keySize);
        }

        public StrongName(byte[] data)
        {
            if (data == null)
                throw new ArgumentNullException("data");

            // check for ECMA key
            if (data.Length == 16)
            {
                int i = 0;
                int sum = 0;
                while (i < data.Length)
                    sum += data[i++];
                if (sum == 4)
                {
                    // it is the ECMA key
                    publicKey = (byte[])data.Clone();
                }
            }
            else
            {
                RSA = CryptoConvert.FromCapiKeyBlob(data);
                if (rsa == null)
                    throw new ArgumentException("data isn't a correctly encoded RSA public key");
            }
        }

        public StrongName(RSA rsa)
        {
            if (rsa == null)
                throw new ArgumentNullException("rsa");

            RSA = rsa;
        }

        private void InvalidateCache()
        {
            publicKey = null;
            keyToken = null;
        }

        public bool CanSign
        {
            get
            {
                if (rsa == null)
                    return false;
#if INSIDE_CORLIB
				// the easy way
				if (RSA is RSACryptoServiceProvider) {
					// available as internal for corlib
					return !(rsa as RSACryptoServiceProvider).PublicOnly;
				}
				else 
#endif
                if (RSA is RSAManaged)
                {
                    return !(rsa as RSAManaged).PublicOnly;
                }
                else
                {
                    // the hard way
                    try
                    {
                        RSAParameters p = rsa.ExportParameters(true);
                        return ((p.D != null) && (p.P != null) && (p.Q != null));
                    }
                    catch (CryptographicException)
                    {
                        return false;
                    }
                }
            }
        }

        public RSA RSA
        {
            get
            {
                // if none then we create a new keypair
                if (rsa == null)
                    rsa = (RSA)RSA.Create();
                return rsa;
            }
            set
            {
                rsa = value;
                InvalidateCache();
            }
        }

        public byte[] PublicKey
        {
            get
            {
                if (publicKey == null)
                {
                    byte[] keyPair = CryptoConvert.ToCapiKeyBlob(rsa, false);
                    // since 2.0 public keys can vary from 384 to 16384 bits
                    publicKey = new byte[32 + (rsa.KeySize >> 3)];

                    // The first 12 bytes are documented at:
                    // http://msdn.microsoft.com/library/en-us/cprefadd/html/grfungethashfromfile.asp
                    // ALG_ID - Signature
                    publicKey[0] = keyPair[4];
                    publicKey[1] = keyPair[5];
                    publicKey[2] = keyPair[6];
                    publicKey[3] = keyPair[7];
                    // ALG_ID - Hash (SHA1 == 0x8004)
                    publicKey[4] = 0x04;
                    publicKey[5] = 0x80;
                    publicKey[6] = 0x00;
                    publicKey[7] = 0x00;
                    // Length of Public Key (in bytes)
                    byte[] lastPart = BitConverterLE.GetBytes(publicKey.Length - 12);
                    publicKey[8] = lastPart[0];
                    publicKey[9] = lastPart[1];
                    publicKey[10] = lastPart[2];
                    publicKey[11] = lastPart[3];
                    // Ok from here - Same structure as keypair - expect for public key
                    publicKey[12] = 0x06;		// PUBLICKEYBLOB
                    // we can copy this part
                    Buffer.BlockCopy(keyPair, 1, publicKey, 13, publicKey.Length - 13);
                    // and make a small adjustment 
                    publicKey[23] = 0x31;		// (RSA1 not RSA2)
                }
                return (byte[])publicKey.Clone();
            }
        }

        public byte[] PublicKeyToken
        {
            get
            {
                if (keyToken == null)
                {
                    byte[] publicKey = PublicKey;
                    if (publicKey == null)
                        return null;
                    HashAlgorithm ha = HashAlgorithm.Create(TokenAlgorithm);
                    byte[] hash = ha.ComputeHash(publicKey);
                    // we need the last 8 bytes in reverse order
                    keyToken = new byte[8];
                    Buffer.BlockCopy(hash, (hash.Length - 8), keyToken, 0, 8);
                    Array.Reverse(keyToken, 0, 8);
                }
                return (byte[])keyToken.Clone();
            }
        }

        public string TokenAlgorithm
        {
            get
            {
                if (tokenAlgorithm == null)
                    tokenAlgorithm = "SHA1";
                return tokenAlgorithm;
            }
            set
            {
                string algo = value.ToUpper(CultureInfo.InvariantCulture);
                if ((algo == "SHA1") || (algo == "MD5"))
                {
                    tokenAlgorithm = value;
                    InvalidateCache();
                }
                else
                    throw new ArgumentException("Unsupported hash algorithm for token");
            }
        }

        public byte[] GetBytes()
        {
            return CryptoConvert.ToCapiPrivateKeyBlob(RSA);
        }

        private UInt32 RVAtoPosition(UInt32 r, int sections, byte[] headers)
        {
            for (int i = 0; i < sections; i++)
            {
                UInt32 p = BitConverterLE.ToUInt32(headers, i * 40 + 20);
                UInt32 s = BitConverterLE.ToUInt32(headers, i * 40 + 12);
                int l = (int)BitConverterLE.ToUInt32(headers, i * 40 + 8);
                if ((s <= r) && (r < s + l))
                {
                    return p + r - s;
                }
            }
            return 0;
        }

        internal StrongNameSignature StrongHash(Stream stream, StrongNameOptions options)
        {
            StrongNameSignature info = new StrongNameSignature();

            HashAlgorithm hash = HashAlgorithm.Create(TokenAlgorithm);
            CryptoStream cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write);

            // MS-DOS Header - always 128 bytes
            // ref: Section 24.2.1, Partition II Metadata
            byte[] mz = new byte[128];
            stream.Read(mz, 0, 128);
            if (BitConverterLE.ToUInt16(mz, 0) != 0x5a4d)
                return null;
            UInt32 peHeader = BitConverterLE.ToUInt32(mz, 60);
            cs.Write(mz, 0, 128);
            if (peHeader != 128)
            {
                byte[] mzextra = new byte[peHeader - 128];
                stream.Read(mzextra, 0, mzextra.Length);
                cs.Write(mzextra, 0, mzextra.Length);
            }

            // PE File Header - always 248 bytes
            // ref: Section 24.2.2, Partition II Metadata
            byte[] pe = new byte[248];
            stream.Read(pe, 0, 248);
            if (BitConverterLE.ToUInt32(pe, 0) != 0x4550)
                return null;
            if (BitConverterLE.ToUInt16(pe, 4) != 0x14c)
                return null;
            // MUST zeroize both CheckSum and Security Directory
            byte[] v = new byte[8];
            Buffer.BlockCopy(v, 0, pe, 88, 4);
            Buffer.BlockCopy(v, 0, pe, 152, 8);
            cs.Write(pe, 0, 248);

            UInt16 numSection = BitConverterLE.ToUInt16(pe, 6);
            int sectionLength = (numSection * 40);
            byte[] sectionHeaders = new byte[sectionLength];
            stream.Read(sectionHeaders, 0, sectionLength);
            cs.Write(sectionHeaders, 0, sectionLength);

            UInt32 cliHeaderRVA = BitConverterLE.ToUInt32(pe, 232);
            UInt32 cliHeaderPos = RVAtoPosition(cliHeaderRVA, numSection, sectionHeaders);
            int cliHeaderSiz = (int)BitConverterLE.ToUInt32(pe, 236);

            // CLI Header
            // ref: Section 24.3.3, Partition II Metadata
            byte[] cli = new byte[cliHeaderSiz];
            stream.Position = cliHeaderPos;
            stream.Read(cli, 0, cliHeaderSiz);

            UInt32 strongNameSignatureRVA = BitConverterLE.ToUInt32(cli, 32);
            info.SignaturePosition = RVAtoPosition(strongNameSignatureRVA, numSection, sectionHeaders);
            info.SignatureLength = BitConverterLE.ToUInt32(cli, 36);

            UInt32 metadataRVA = BitConverterLE.ToUInt32(cli, 8);
            info.MetadataPosition = RVAtoPosition(metadataRVA, numSection, sectionHeaders);
            info.MetadataLength = BitConverterLE.ToUInt32(cli, 12);

            if (options == StrongNameOptions.Metadata)
            {
                cs.Close();
                hash.Initialize();
                byte[] metadata = new byte[info.MetadataLength];
                stream.Position = info.MetadataPosition;
                stream.Read(metadata, 0, metadata.Length);
                info.Hash = hash.ComputeHash(metadata);
                return info;
            }

            // now we hash every section EXCEPT the signature block
            for (int i = 0; i < numSection; i++)
            {
                UInt32 start = BitConverterLE.ToUInt32(sectionHeaders, i * 40 + 20);
                int length = (int)BitConverterLE.ToUInt32(sectionHeaders, i * 40 + 16);
                byte[] section = new byte[length];
                stream.Position = start;
                stream.Read(section, 0, length);
                if ((start <= info.SignaturePosition) && (info.SignaturePosition < start + length))
                {
                    // hash before the signature
                    int before = (int)(info.SignaturePosition - start);
                    if (before > 0)
                    {
                        cs.Write(section, 0, before);
                    }
                    // copy signature
                    info.Signature = new byte[info.SignatureLength];
                    Buffer.BlockCopy(section, before, info.Signature, 0, (int)info.SignatureLength);
                    Array.Reverse(info.Signature);
                    // hash after the signature
                    int s = (int)(before + info.SignatureLength);
                    int after = (int)(length - s);
                    if (after > 0)
                    {
                        cs.Write(section, s, after);
                    }
                }
                else
                    cs.Write(section, 0, length);
            }

            cs.Close();
            info.Hash = hash.Hash;
            return info;
        }

        // return the same result as the undocumented and unmanaged GetHashFromAssemblyFile
        public byte[] Hash(string fileName)
        {
            FileStream fs = File.OpenRead(fileName);
            StrongNameSignature sn = StrongHash(fs, StrongNameOptions.Metadata);
            fs.Close();

            return sn.Hash;
        }

        public bool Sign(string fileName)
        {
            bool result = false;
            StrongNameSignature sn;
            using (FileStream fs = File.OpenRead(fileName))
            {
                sn = StrongHash(fs, StrongNameOptions.Signature);
                fs.Close();
            }
            if (sn.Hash == null)
                return false;

            byte[] signature = null;
            try
            {
                RSAPKCS1SignatureFormatter sign = new RSAPKCS1SignatureFormatter(rsa);
                sign.SetHashAlgorithm(TokenAlgorithm);
                signature = sign.CreateSignature(sn.Hash);
                Array.Reverse(signature);
            }
            catch (CryptographicException)
            {
                return false;
            }

            using (FileStream fs = File.OpenWrite(fileName))
            {
                fs.Position = sn.SignaturePosition;
                fs.Write(signature, 0, signature.Length);
                fs.Close();
                result = true;
            }
            return result;
        }

        public bool Verify(string fileName)
        {
            StrongNameSignature sn;
            using (FileStream fs = File.OpenRead(fileName))
            {
                sn = StrongHash(fs, StrongNameOptions.Signature);
                fs.Close();
            }
            if (sn.Hash == null)
            {
                return false;
            }

            try
            {
                AssemblyHashAlgorithm algorithm = AssemblyHashAlgorithm.SHA1;
                if (tokenAlgorithm == "MD5")
                    algorithm = AssemblyHashAlgorithm.MD5;
                return Verify(rsa, algorithm, sn.Hash, sn.Signature);
            }
            catch (CryptographicException)
            {
                // no exception allowed
                return false;
            }
        }

#if INSIDE_CORLIB
		static object lockObject = new object ();
		static bool initialized = false;

		// We don't want a dependency on StrongNameManager in Mono.Security.dll
		static public bool IsAssemblyStrongnamed (string assemblyName) 
		{
			if (!initialized) {
				lock (lockObject) {
					if (!initialized) {
						string config = Environment.GetMachineConfigPath ();
						StrongNameManager.LoadConfig (config);
						initialized = true;
					}
				}
			}

			try {
				// this doesn't load the assembly (well it unloads it ;)
				// http://weblogs.asp.net/nunitaddin/posts/9991.aspx
				AssemblyName an = AssemblyName.GetAssemblyName (assemblyName);
				if (an == null)
					return false;

				byte[] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
				if ((publicKey == null) || (publicKey.Length < 12)) {
					// no mapping
					publicKey = an.GetPublicKey ();
					if ((publicKey == null) || (publicKey.Length < 12))
						return false;
				}

				// Note: MustVerify is based on the original token (by design). Public key
				// remapping won't affect if the assembly is verified or not.
				if (!StrongNameManager.MustVerify (an)) {
					return true;
				}

				RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
				StrongName sn = new StrongName (rsa);
				bool result = sn.Verify (assemblyName);
				return result;
			}
			catch {
				// no exception allowed
				return false;
			}
		}

		// TODO
		// we would get better performance if the runtime hashed the
		// assembly - as we wouldn't have to load it from disk a 
		// second time. The runtime already have implementations of
		// SHA1 (and even MD5 if required someday).
		static public bool VerifySignature (byte[] publicKey, int algorithm, byte[] hash, byte[] signature) 
		{
			try {
				RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey);
				return Verify (rsa, (AssemblyHashAlgorithm) algorithm, hash, signature);
			}
			catch {
				// no exception allowed
				return false;
			}
		}
#endif
        static private bool Verify(RSA rsa, AssemblyHashAlgorithm algorithm, byte[] hash, byte[] signature)
        {
            RSAPKCS1SignatureDeformatter vrfy = new RSAPKCS1SignatureDeformatter(rsa);
            switch (algorithm)
            {
                case AssemblyHashAlgorithm.MD5:
                    vrfy.SetHashAlgorithm("MD5");
                    break;
                case AssemblyHashAlgorithm.SHA1:
                case AssemblyHashAlgorithm.None:
                default:
                    vrfy.SetHashAlgorithm("SHA1");
                    break;
            }
            return vrfy.VerifySignature(hash, signature);
        }
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 sealed class PKCS7
    {

        public class Oid
        {
            // pkcs 1
            public const string rsaEncryption = "1.2.840.113549.1.1.1";
            // pkcs 7
            public const string data = "1.2.840.113549.1.7.1";
            public const string signedData = "1.2.840.113549.1.7.2";
            public const string envelopedData = "1.2.840.113549.1.7.3";
            public const string signedAndEnvelopedData = "1.2.840.113549.1.7.4";
            public const string digestedData = "1.2.840.113549.1.7.5";
            public const string encryptedData = "1.2.840.113549.1.7.6";
            // pkcs 9
            public const string contentType = "1.2.840.113549.1.9.3";
            public const string messageDigest = "1.2.840.113549.1.9.4";
            public const string signingTime = "1.2.840.113549.1.9.5";
            public const string countersignature = "1.2.840.113549.1.9.6";

            public Oid()
            {
            }
        }

        private PKCS7()
        {
        }

        static public ASN1 Attribute(string oid, ASN1 value)
        {
            ASN1 attr = new ASN1(0x30);
            attr.Add(ASN1Convert.FromOid(oid));
            ASN1 aset = attr.Add(new ASN1(0x31));
            aset.Add(value);
            return attr;
        }

        static public ASN1 AlgorithmIdentifier(string oid)
        {
            ASN1 ai = new ASN1(0x30);
            ai.Add(ASN1Convert.FromOid(oid));
            ai.Add(new ASN1(0x05));	// NULL
            return ai;
        }

        static public ASN1 AlgorithmIdentifier(string oid, ASN1 parameters)
        {
            ASN1 ai = new ASN1(0x30);
            ai.Add(ASN1Convert.FromOid(oid));
            ai.Add(parameters);
            return ai;
        }

        /*
         * IssuerAndSerialNumber ::= SEQUENCE {
         *	issuer Name,
         *	serialNumber CertificateSerialNumber 
         * }
         */
        static public ASN1 IssuerAndSerialNumber(X509Certificate x509)
        {
            ASN1 issuer = null;
            ASN1 serial = null;
            ASN1 cert = new ASN1(x509.RawData);
            int tbs = 0;
            bool flag = false;
            while (tbs < cert[0].Count)
            {
                ASN1 e = cert[0][tbs++];
                if (e.Tag == 0x02)
                    serial = e;
                else if (e.Tag == 0x30)
                {
                    if (flag)
                    {
                        issuer = e;
                        break;
                    }
                    flag = true;
                }
            }
            ASN1 iasn = new ASN1(0x30);
            iasn.Add(issuer);
            iasn.Add(serial);
            return iasn;
        }

        /*
         * ContentInfo ::= SEQUENCE {
         *	contentType ContentType,
         *	content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL 
         * }
         * ContentType ::= OBJECT IDENTIFIER
         */
        public class ContentInfo
        {

            private string contentType;
            private ASN1 content;

            public ContentInfo()
            {
                content = new ASN1(0xA0);
            }

            public ContentInfo(string oid)
                : this()
            {
                contentType = oid;
            }

            public ContentInfo(byte[] data)
                : this(new ASN1(data)) { }

            public ContentInfo(ASN1 asn1)
            {
                // SEQUENCE with 1 or 2 elements
                if ((asn1.Tag != 0x30) || ((asn1.Count < 1) && (asn1.Count > 2)))
                    throw new ArgumentException("Invalid ASN1");
                if (asn1[0].Tag != 0x06)
                    throw new ArgumentException("Invalid contentType");
                contentType = ASN1Convert.ToOid(asn1[0]);
                if (asn1.Count > 1)
                {
                    if (asn1[1].Tag != 0xA0)
                        throw new ArgumentException("Invalid content");
                    content = asn1[1];
                }
            }

            public ASN1 ASN1
            {
                get { return GetASN1(); }
            }

            public ASN1 Content
            {
                get { return content; }
                set { content = value; }
            }

            public string ContentType
            {
                get { return contentType; }
                set { contentType = value; }
            }

            internal ASN1 GetASN1()
            {
                // ContentInfo ::= SEQUENCE {
                ASN1 contentInfo = new ASN1(0x30);
                // contentType ContentType, -> ContentType ::= OBJECT IDENTIFIER
                contentInfo.Add(ASN1Convert.FromOid(contentType));
                // content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL 
                if ((content != null) && (content.Count > 0))
                    contentInfo.Add(content);
                return contentInfo;
            }

            public byte[] GetBytes()
            {
                return GetASN1().GetBytes();
            }
        }

        /*
         * EncryptedData ::= SEQUENCE {
         *	version		INTEGER {edVer0(0)} (edVer0),
         *	 encryptedContentInfo  EncryptedContentInfo
         * }
         */
        public class EncryptedData
        {
            private byte _version;
            private ContentInfo _content;
            private ContentInfo _encryptionAlgorithm;
            private byte[] _encrypted;

            public EncryptedData()
            {
                _version = 0;
            }

            public EncryptedData(byte[] data)
                : this(new ASN1(data))
            {
            }

            public EncryptedData(ASN1 asn1)
                : this()
            {
                if ((asn1.Tag != 0x30) || (asn1.Count < 2))
                    throw new ArgumentException("Invalid EncryptedData");

                if (asn1[0].Tag != 0x02)
                    throw new ArgumentException("Invalid version");
                _version = asn1[0].Value[0];

                ASN1 encryptedContentInfo = asn1[1];
                if (encryptedContentInfo.Tag != 0x30)
                    throw new ArgumentException("missing EncryptedContentInfo");

                ASN1 contentType = encryptedContentInfo[0];
                if (contentType.Tag != 0x06)
                    throw new ArgumentException("missing EncryptedContentInfo.ContentType");
                _content = new ContentInfo(ASN1Convert.ToOid(contentType));

                ASN1 contentEncryptionAlgorithm = encryptedContentInfo[1];
                if (contentEncryptionAlgorithm.Tag != 0x30)
                    throw new ArgumentException("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
                _encryptionAlgorithm = new ContentInfo(ASN1Convert.ToOid(contentEncryptionAlgorithm[0]));
                _encryptionAlgorithm.Content = contentEncryptionAlgorithm[1];

                ASN1 encryptedContent = encryptedContentInfo[2];
                if (encryptedContent.Tag != 0x80)
                    throw new ArgumentException("missing EncryptedContentInfo.EncryptedContent");
                _encrypted = encryptedContent.Value;
            }

            public ASN1 ASN1
            {
                get { return GetASN1(); }
            }

            public ContentInfo ContentInfo
            {
                get { return _content; }
            }

            public ContentInfo EncryptionAlgorithm
            {
                get { return _encryptionAlgorithm; }
            }

            public byte[] EncryptedContent
            {
                get
                {
                    if (_encrypted == null)
                        return null;
                    return (byte[])_encrypted.Clone();
                }
            }

            public byte Version
            {
                get { return _version; }
                set { _version = value; }
            }

            // methods

            internal ASN1 GetASN1()
            {
                return null;
            }

            public byte[] GetBytes()
            {
                return GetASN1().GetBytes();
            }
        }

        /*
         * EnvelopedData ::= SEQUENCE {
         *	version Version,
         *	recipientInfos RecipientInfos,
         *	encryptedContentInfo EncryptedContentInfo 
         * }
         * 
         * RecipientInfos ::= SET OF RecipientInfo
         * 
         * EncryptedContentInfo ::= SEQUENCE {
         *	contentType ContentType,
         *	contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
         *	encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL 
         * }
         * 
         * EncryptedContent ::= OCTET STRING
         * 
         */
        public class EnvelopedData
        {
            private byte _version;
            private ContentInfo _content;
            private ContentInfo _encryptionAlgorithm;
            private ArrayList _recipientInfos;
            private byte[] _encrypted;

            public EnvelopedData()
            {
                _version = 0;
                _content = new ContentInfo();
                _encryptionAlgorithm = new ContentInfo();
                _recipientInfos = new ArrayList();
            }

            public EnvelopedData(byte[] data)
                : this(new ASN1(data))
            {
            }

            public EnvelopedData(ASN1 asn1)
                : this()
            {
                if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 3))
                    throw new ArgumentException("Invalid EnvelopedData");

                if (asn1[0][0].Tag != 0x02)
                    throw new ArgumentException("Invalid version");
                _version = asn1[0][0].Value[0];

                // recipientInfos

                ASN1 recipientInfos = asn1[0][1];
                if (recipientInfos.Tag != 0x31)
                    throw new ArgumentException("missing RecipientInfos");
                for (int i = 0; i < recipientInfos.Count; i++)
                {
                    ASN1 recipientInfo = recipientInfos[i];
                    _recipientInfos.Add(new RecipientInfo(recipientInfo));
                }

                ASN1 encryptedContentInfo = asn1[0][2];
                if (encryptedContentInfo.Tag != 0x30)
                    throw new ArgumentException("missing EncryptedContentInfo");

                ASN1 contentType = encryptedContentInfo[0];
                if (contentType.Tag != 0x06)
                    throw new ArgumentException("missing EncryptedContentInfo.ContentType");
                _content = new ContentInfo(ASN1Convert.ToOid(contentType));

                ASN1 contentEncryptionAlgorithm = encryptedContentInfo[1];
                if (contentEncryptionAlgorithm.Tag != 0x30)
                    throw new ArgumentException("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
                _encryptionAlgorithm = new ContentInfo(ASN1Convert.ToOid(contentEncryptionAlgorithm[0]));
                _encryptionAlgorithm.Content = contentEncryptionAlgorithm[1];

                ASN1 encryptedContent = encryptedContentInfo[2];
                if (encryptedContent.Tag != 0x80)
                    throw new ArgumentException("missing EncryptedContentInfo.EncryptedContent");
                _encrypted = encryptedContent.Value;
            }

            public ArrayList RecipientInfos
            {
                get { return _recipientInfos; }
            }

            public ASN1 ASN1
            {
                get { return GetASN1(); }
            }

            public ContentInfo ContentInfo
            {
                get { return _content; }
            }

            public ContentInfo EncryptionAlgorithm
            {
                get { return _encryptionAlgorithm; }
            }

            public byte[] EncryptedContent
            {
                get
                {
                    if (_encrypted == null)
                        return null;
                    return (byte[])_encrypted.Clone();
                }
            }

            public byte Version
            {
                get { return _version; }
                set { _version = value; }
            }

            internal ASN1 GetASN1()
            {
                // SignedData ::= SEQUENCE {
                ASN1 signedData = new ASN1(0x30);
                // version Version -> Version ::= INTEGER
                /*				byte[] ver = { _version };
                                signedData.Add (new ASN1 (0x02, ver));
                                // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
                                ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31));
                                if (hashAlgorithm != null) {
                                    string hashOid = CryptoConfig.MapNameToOid (hashAlgorithm);
                                    digestAlgorithms.Add (AlgorithmIdentifier (hashOid));
                                }

                                // contentInfo ContentInfo,
                                ASN1 ci = contentInfo.ASN1;
                                signedData.Add (ci);
                                if ((mda == null) && (hashAlgorithm != null)) {
                                    // automatically add the messageDigest authenticated attribute
                                    HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
                                    byte[] idcHash = ha.ComputeHash (ci[1][0].Value);
                                    ASN1 md = new ASN1 (0x30);
                                    mda = Attribute (messageDigest, md.Add (new ASN1 (0x04, idcHash)));
                                    signerInfo.AuthenticatedAttributes.Add (mda);
                                }

                                // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
                                if (certs.Count > 0) {
                                    ASN1 a0 = signedData.Add (new ASN1 (0xA0));
                                    foreach (X509Certificate x in certs)
                                        a0.Add (new ASN1 (x.RawData));
                                }
                                // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
                                if (crls.Count > 0) {
                                    ASN1 a1 = signedData.Add (new ASN1 (0xA1));
                                    foreach (byte[] crl in crls)
                                        a1.Add (new ASN1 (crl));
                                }
                                // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo
                                ASN1 signerInfos = signedData.Add (new ASN1 (0x31));
                                if (signerInfo.Key != null)
                                    signerInfos.Add (signerInfo.ASN1);*/
                return signedData;
            }

            public byte[] GetBytes()
            {
                return GetASN1().GetBytes();
            }
        }

        /* RecipientInfo ::= SEQUENCE {
         *	version Version,
         *	issuerAndSerialNumber IssuerAndSerialNumber,
         *	keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
         *	encryptedKey EncryptedKey 
         * }
         * 
         * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
         * 
         * EncryptedKey ::= OCTET STRING
         */
        public class RecipientInfo
        {

            private int _version;
            private string _oid;
            private byte[] _key;
            private byte[] _ski;
            private string _issuer;
            private byte[] _serial;

            public RecipientInfo() { }

            public RecipientInfo(ASN1 data)
            {
                if (data.Tag != 0x30)
                    throw new ArgumentException("Invalid RecipientInfo");

                ASN1 version = data[0];
                if (version.Tag != 0x02)
                    throw new ArgumentException("missing Version");
                _version = version.Value[0];

                // issuerAndSerialNumber IssuerAndSerialNumber
                ASN1 subjectIdentifierType = data[1];
                if ((subjectIdentifierType.Tag == 0x80) && (_version == 3))
                {
                    _ski = subjectIdentifierType.Value;
                }
                else
                {
                    _issuer = X501.ToString(subjectIdentifierType[0]);
                    _serial = subjectIdentifierType[1].Value;
                }

                ASN1 keyEncryptionAlgorithm = data[2];
                _oid = ASN1Convert.ToOid(keyEncryptionAlgorithm[0]);

                ASN1 encryptedKey = data[3];
                _key = encryptedKey.Value;
            }

            public string Oid
            {
                get { return _oid; }
            }

            public byte[] Key
            {
                get
                {
                    if (_key == null)
                        return null;
                    return (byte[])_key.Clone();
                }
            }

            public byte[] SubjectKeyIdentifier
            {
                get
                {
                    if (_ski == null)
                        return null;
                    return (byte[])_ski.Clone();
                }
            }

            public string Issuer
            {
                get { return _issuer; }
            }

            public byte[] Serial
            {
                get
                {
                    if (_serial == null)
                        return null;
                    return (byte[])_serial.Clone();
                }
            }

            public int Version
            {
                get { return _version; }
            }
        }

        /*
         * SignedData ::= SEQUENCE {
         *	version Version,
         *	digestAlgorithms DigestAlgorithmIdentifiers,
         *	contentInfo ContentInfo,
         *	certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
         *	crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
         *	signerInfos SignerInfos 
         * }
         */
        public class SignedData
        {
            private byte version;
            private string hashAlgorithm;
            private ContentInfo contentInfo;
            private X509CertificateCollection certs;
            private ArrayList crls;
            private SignerInfo signerInfo;
            private bool mda;
            private bool signed;

            public SignedData()
            {
                version = 1;
                contentInfo = new ContentInfo();
                certs = new X509CertificateCollection();
                crls = new ArrayList();
                signerInfo = new SignerInfo();
                mda = true;
                signed = false;
            }

            public SignedData(byte[] data)
                : this(new ASN1(data))
            {
            }

            public SignedData(ASN1 asn1)
            {
                if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 4))
                    throw new ArgumentException("Invalid SignedData");

                if (asn1[0][0].Tag != 0x02)
                    throw new ArgumentException("Invalid version");
                version = asn1[0][0].Value[0];

                contentInfo = new ContentInfo(asn1[0][2]);

                int n = 3;
                certs = new X509CertificateCollection();
                if (asn1[0][n].Tag == 0xA0)
                {
                    for (int i = 0; i < asn1[0][n].Count; i++)
                        certs.Add(new X509Certificate(asn1[0][n][i].GetBytes()));
                    n++;
                }

                crls = new ArrayList();
                if (asn1[0][n].Tag == 0xA1)
                {
                    for (int i = 0; i < asn1[0][n].Count; i++)
                        crls.Add(asn1[0][n][i].GetBytes());
                    n++;
                }

                if (asn1[0][n].Count > 0)
                    signerInfo = new SignerInfo(asn1[0][n]);
                else
                    signerInfo = new SignerInfo();

                // Exchange hash algorithm Oid from SignerInfo
                if (signerInfo.HashName != null)
                {
                    HashName = OidToName(signerInfo.HashName);
                }

                // Check if SignerInfo has authenticated attributes
                mda = (signerInfo.AuthenticatedAttributes.Count > 0);
            }

            public ASN1 ASN1
            {
                get { return GetASN1(); }
            }

            public X509CertificateCollection Certificates
            {
                get { return certs; }
            }

            public ContentInfo ContentInfo
            {
                get { return contentInfo; }
            }

            public ArrayList Crls
            {
                get { return crls; }
            }

            public string HashName
            {
                get { return hashAlgorithm; }
                // todo add validation
                set
                {
                    hashAlgorithm = value;
                    signerInfo.HashName = value;
                }
            }

            public SignerInfo SignerInfo
            {
                get { return signerInfo; }
            }

            public byte Version
            {
                get { return version; }
                set { version = value; }
            }

            public bool UseAuthenticatedAttributes
            {
                get { return mda; }
                set { mda = value; }
            }

            public bool VerifySignature(AsymmetricAlgorithm aa)
            {
                if (aa == null)
                {
                    return false;
                }

                RSAPKCS1SignatureDeformatter r = new RSAPKCS1SignatureDeformatter(aa);
                r.SetHashAlgorithm(hashAlgorithm);
                HashAlgorithm ha = HashAlgorithm.Create(hashAlgorithm);

                byte[] signature = signerInfo.Signature;
                byte[] hash = null;

                if (mda)
                {
                    ASN1 asn = new ASN1(0x31);
                    foreach (ASN1 attr in signerInfo.AuthenticatedAttributes)
                        asn.Add(attr);

                    hash = ha.ComputeHash(asn.GetBytes());
                }
                else
                {
                    hash = ha.ComputeHash(contentInfo.Content[0].Value);
                }

                if (hash != null && signature != null)
                {
                    return r.VerifySignature(hash, signature);
                }
                return false;
            }

            internal string OidToName(string oid)
            {
                switch (oid)
                {
                    case "1.3.14.3.2.26":
                        return "SHA1";
                    case "1.2.840.113549.2.2":
                        return "MD2";
                    case "1.2.840.113549.2.5":
                        return "MD5";
                    case "2.16.840.1.101.3.4.1":
                        return "SHA256";
                    case "2.16.840.1.101.3.4.2":
                        return "SHA384";
                    case "2.16.840.1.101.3.4.3":
                        return "SHA512";
                    default:
                        break;
                }
                // Unknown Oid
                return oid;
            }

            internal ASN1 GetASN1()
            {
                // SignedData ::= SEQUENCE {
                ASN1 signedData = new ASN1(0x30);
                // version Version -> Version ::= INTEGER
                byte[] ver = { version };
                signedData.Add(new ASN1(0x02, ver));
                // digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
                ASN1 digestAlgorithms = signedData.Add(new ASN1(0x31));
                if (hashAlgorithm != null)
                {
                    string hashOid = CryptoConfig.MapNameToOID(hashAlgorithm);
                    digestAlgorithms.Add(AlgorithmIdentifier(hashOid));
                }

                // contentInfo ContentInfo,
                ASN1 ci = contentInfo.ASN1;
                signedData.Add(ci);
                if (!signed && (hashAlgorithm != null))
                {
                    if (mda)
                    {
                        // Use authenticated attributes for signature

                        // Automatically add the contentType authenticated attribute
                        ASN1 ctattr = Attribute(Oid.contentType, ci[0]);
                        signerInfo.AuthenticatedAttributes.Add(ctattr);

                        // Automatically add the messageDigest authenticated attribute
                        HashAlgorithm ha = HashAlgorithm.Create(hashAlgorithm);
                        byte[] idcHash = ha.ComputeHash(ci[1][0].Value);
                        ASN1 md = new ASN1(0x30);
                        ASN1 mdattr = Attribute(Oid.messageDigest, md.Add(new ASN1(0x04, idcHash)));
                        signerInfo.AuthenticatedAttributes.Add(mdattr);
                    }
                    else
                    {
                        // Don't use authenticated attributes for signature -- signature is content
                        RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter(signerInfo.Key);
                        r.SetHashAlgorithm(hashAlgorithm);
                        HashAlgorithm ha = HashAlgorithm.Create(hashAlgorithm);
                        byte[] sig = ha.ComputeHash(ci[1][0].Value);
                        signerInfo.Signature = r.CreateSignature(sig);
                    }
                    signed = true;
                }

                // certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
                if (certs.Count > 0)
                {
                    ASN1 a0 = signedData.Add(new ASN1(0xA0));
                    foreach (X509Certificate x in certs)
                        a0.Add(new ASN1(x.RawData));
                }
                // crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
                if (crls.Count > 0)
                {
                    ASN1 a1 = signedData.Add(new ASN1(0xA1));
                    foreach (byte[] crl in crls)
                        a1.Add(new ASN1(crl));
                }
                // signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo
                ASN1 signerInfos = signedData.Add(new ASN1(0x31));
                if (signerInfo.Key != null)
                    signerInfos.Add(signerInfo.ASN1);
                return signedData;
            }

            public byte[] GetBytes()
            {
                return GetASN1().GetBytes();
            }
        }

        /*
         * SignerInfo ::= SEQUENCE {
         *	version Version,
         * 	issuerAndSerialNumber IssuerAndSerialNumber,
         * 	digestAlgorithm DigestAlgorithmIdentifier,
         * 	authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
         * 	digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
         * 	encryptedDigest EncryptedDigest,
         * 	unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL 
         * }
         * 
         * For version == 3 issuerAndSerialNumber may be replaced by ...
         */
        public class SignerInfo
        {

            private byte version;
            private X509Certificate x509;
            private string hashAlgorithm;
            private AsymmetricAlgorithm key;
            private ArrayList authenticatedAttributes;
            private ArrayList unauthenticatedAttributes;
            private byte[] signature;
            private string issuer;
            private byte[] serial;
            private byte[] ski;

            public SignerInfo()
            {
                version = 1;
                authenticatedAttributes = new ArrayList();
                unauthenticatedAttributes = new ArrayList();
            }

            public SignerInfo(byte[] data)
                : this(new ASN1(data)) { }

            // TODO: INCOMPLETE
            public SignerInfo(ASN1 asn1)
                : this()
            {
                if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 5))
                    throw new ArgumentException("Invalid SignedData");

                // version Version
                if (asn1[0][0].Tag != 0x02)
                    throw new ArgumentException("Invalid version");
                version = asn1[0][0].Value[0];

                // issuerAndSerialNumber IssuerAndSerialNumber
                ASN1 subjectIdentifierType = asn1[0][1];
                if ((subjectIdentifierType.Tag == 0x80) && (version == 3))
                {
                    ski = subjectIdentifierType.Value;
                }
                else
                {
                    issuer = X501.ToString(subjectIdentifierType[0]);
                    serial = subjectIdentifierType[1].Value;
                }

                // digestAlgorithm DigestAlgorithmIdentifier
                ASN1 digestAlgorithm = asn1[0][2];
                hashAlgorithm = ASN1Convert.ToOid(digestAlgorithm[0]);

                // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL
                int n = 3;
                ASN1 authAttributes = asn1[0][n];
                if (authAttributes.Tag == 0xA0)
                {
                    n++;
                    for (int i = 0; i < authAttributes.Count; i++)
                        authenticatedAttributes.Add(authAttributes[i]);
                }

                // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier
                n++;
                // ASN1 digestEncryptionAlgorithm = asn1 [0][n++];
                // string digestEncryptionAlgorithmOid = ASN1Convert.ToOid (digestEncryptionAlgorithm [0]);

                // encryptedDigest EncryptedDigest
                ASN1 encryptedDigest = asn1[0][n++];
                if (encryptedDigest.Tag == 0x04)
                    signature = encryptedDigest.Value;

                // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
                ASN1 unauthAttributes = asn1[0][n];
                if ((unauthAttributes != null) && (unauthAttributes.Tag == 0xA1))
                {
                    for (int i = 0; i < unauthAttributes.Count; i++)
                        unauthenticatedAttributes.Add(unauthAttributes[i]);
                }
            }

            public string IssuerName
            {
                get { return issuer; }
            }

            public byte[] SerialNumber
            {
                get
                {
                    if (serial == null)
                        return null;
                    return (byte[])serial.Clone();
                }
            }

            public byte[] SubjectKeyIdentifier
            {
                get
                {
                    if (ski == null)
                        return null;
                    return (byte[])ski.Clone();
                }
            }

            public ASN1 ASN1
            {
                get { return GetASN1(); }
            }

            public ArrayList AuthenticatedAttributes
            {
                get { return authenticatedAttributes; }
            }

            public X509Certificate Certificate
            {
                get { return x509; }
                set { x509 = value; }
            }

            public string HashName
            {
                get { return hashAlgorithm; }
                set { hashAlgorithm = value; }
            }

            public AsymmetricAlgorithm Key
            {
                get { return key; }
                set { key = value; }
            }

            public byte[] Signature
            {
                get
                {
                    if (signature == null)
                        return null;
                    return (byte[])signature.Clone();
                }

                set
                {
                    if (value != null)
                    {
                        signature = (byte[])value.Clone();
                    }
                }
            }

            public ArrayList UnauthenticatedAttributes
            {
                get { return unauthenticatedAttributes; }
            }

            public byte Version
            {
                get { return version; }
                set { version = value; }
            }

            internal ASN1 GetASN1()
            {
                if ((key == null) || (hashAlgorithm == null))
                    return null;
                byte[] ver = { version };
                ASN1 signerInfo = new ASN1(0x30);
                // version Version -> Version ::= INTEGER
                signerInfo.Add(new ASN1(0x02, ver));
                // issuerAndSerialNumber IssuerAndSerialNumber,
                signerInfo.Add(PKCS7.IssuerAndSerialNumber(x509));
                // digestAlgorithm DigestAlgorithmIdentifier,
                string hashOid = CryptoConfig.MapNameToOID(hashAlgorithm);
                signerInfo.Add(AlgorithmIdentifier(hashOid));
                // authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
                ASN1 aa = null;
                if (authenticatedAttributes.Count > 0)
                {
                    aa = signerInfo.Add(new ASN1(0xA0));
                    foreach (ASN1 attr in authenticatedAttributes)
                        aa.Add(attr);
                }
                // digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
                if (key is RSA)
                {
                    signerInfo.Add(AlgorithmIdentifier(PKCS7.Oid.rsaEncryption));

                    if (aa != null)
                    {
                        // Calculate the signature here; otherwise it must be set from SignedData
                        RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter(key);
                        r.SetHashAlgorithm(hashAlgorithm);
                        byte[] tbs = aa.GetBytes();
                        tbs[0] = 0x31; // not 0xA0 for signature
                        HashAlgorithm ha = HashAlgorithm.Create(hashAlgorithm);
                        byte[] tbsHash = ha.ComputeHash(tbs);
                        signature = r.CreateSignature(tbsHash);
                    }
                }
                else if (key is DSA)
                {
                    throw new NotImplementedException("not yet");
                }
                else
                    throw new CryptographicException("Unknown assymetric algorithm");
                // encryptedDigest EncryptedDigest,
                signerInfo.Add(new ASN1(0x04, signature));
                // unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL 
                if (unauthenticatedAttributes.Count > 0)
                {
                    ASN1 ua = signerInfo.Add(new ASN1(0xA1));
                    foreach (ASN1 attr in unauthenticatedAttributes)
                        ua.Add(attr);
                }
                return signerInfo;
            }

            public byte[] GetBytes()
            {
                return GetASN1().GetBytes();
            }
        }
    }

    // References:
    // a.	ITU ASN.1 standards (free download)
    //	http://www.itu.int/ITU-T/studygroups/com17/languages/

#if INSIDE_CORLIB
	internal
#else
    public
#endif
#if NET_2_0
	static class ASN1Convert {
#else
 sealed class ASN1Convert
    {

        private ASN1Convert()
        {
        }
#endif
        // RFC3280, section 4.2.1.5
        // CAs conforming to this profile MUST always encode certificate
        // validity dates through the year 2049 as UTCTime; certificate validity
        // dates in 2050 or later MUST be encoded as GeneralizedTime.
        static public ASN1 FromDateTime(DateTime dt)
        {
            if (dt.Year < 2050)
            {
                // UTCTIME
                return new ASN1(0x17, Encoding.ASCII.GetBytes(
                    dt.ToUniversalTime().ToString("yyMMddHHmmss",
                    CultureInfo.InvariantCulture) + "Z"));
            }
            else
            {
                // GENERALIZEDTIME
                return new ASN1(0x18, Encoding.ASCII.GetBytes(
                    dt.ToUniversalTime().ToString("yyyyMMddHHmmss",
                    CultureInfo.InvariantCulture) + "Z"));
            }
        }

        static public ASN1 FromInt32(Int32 value)
        {
            byte[] integer = BitConverterLE.GetBytes(value);
            Array.Reverse(integer);
            int x = 0;
            while ((x < integer.Length) && (integer[x] == 0x00))
                x++;
            ASN1 asn1 = new ASN1(0x02);
            switch (x)
            {
                case 0:
                    asn1.Value = integer;
                    break;
                case 4:
                    asn1.Value = new byte[1];
                    break;
                default:
                    byte[] smallerInt = new byte[4 - x];
                    Buffer.BlockCopy(integer, x, smallerInt, 0, smallerInt.Length);
                    asn1.Value = smallerInt;
                    break;
            }
            return asn1;
        }

        static public ASN1 FromOid(string oid)
        {
            if (oid == null)
                throw new ArgumentNullException("oid");

            return new ASN1(CryptoConfig.EncodeOID(oid));
        }

        static public ASN1 FromUnsignedBigInteger(byte[] big)
        {
            if (big == null)
                throw new ArgumentNullException("big");

            // check for numbers that could be interpreted as negative (first bit)
            if (big[0] >= 0x80)
            {
                // in thie cas we add a new, empty, byte (position 0) so we're
                // sure this will always be interpreted an unsigned integer.
                // However we can't feed it into RSAParameters or DSAParameters
                int length = big.Length + 1;
                byte[] uinteger = new byte[length];
                Buffer.BlockCopy(big, 0, uinteger, 1, length - 1);
                big = uinteger;
            }
            return new ASN1(0x02, big);
        }

        static public int ToInt32(ASN1 asn1)
        {
            if (asn1 == null)
                throw new ArgumentNullException("asn1");
            if (asn1.Tag != 0x02)
                throw new FormatException("Only integer can be converted");

            int x = 0;
            for (int i = 0; i < asn1.Value.Length; i++)
                x = (x << 8) + asn1.Value[i];
            return x;
        }

        // Convert a binary encoded OID to human readable string representation of 
        // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann.
        static public string ToOid(ASN1 asn1)
        {
            if (asn1 == null)
                throw new ArgumentNullException("asn1");

            byte[] aOID = asn1.Value;
            StringBuilder sb = new StringBuilder();
            // Pick apart the OID
            byte x = (byte)(aOID[0] / 40);
            byte y = (byte)(aOID[0] % 40);
            if (x > 2)
            {
                // Handle special case for large y if x = 2
                y += (byte)((x - 2) * 40);
                x = 2;
            }
            sb.Append(x.ToString(CultureInfo.InvariantCulture));
            sb.Append(".");
            sb.Append(y.ToString(CultureInfo.InvariantCulture));
            ulong val = 0;
            for (x = 1; x < aOID.Length; x++)
            {
                val = ((val << 7) | ((byte)(aOID[x] & 0x7F)));
                if (!((aOID[x] & 0x80) == 0x80))
                {
                    sb.Append(".");
                    sb.Append(val.ToString(CultureInfo.InvariantCulture));
                    val = 0;
                }
            }
            return sb.ToString();
        }

        static public DateTime ToDateTime(ASN1 time)
        {
            if (time == null)
                throw new ArgumentNullException("time");

            string t = Encoding.ASCII.GetString(time.Value);
            // to support both UTCTime and GeneralizedTime (and not so common format)
            string mask = null;
            switch (t.Length)
            {
                case 11:
                    mask = "yyMMddHHmmZ"; // illegal I think ... must check
                    break;
                case 13:
                    // RFC3280: 4.1.2.5.1  UTCTime
                    int year = Convert.ToInt16(t.Substring(0, 2), CultureInfo.InvariantCulture);
                    // Where YY is greater than or equal to 50, the 
                    // year SHALL be interpreted as 19YY; and 
                    // Where YY is less than 50, the year SHALL be 
                    // interpreted as 20YY.
                    if (year >= 50)
                        t = "19" + t;
                    else
                        t = "20" + t;
                    mask = "yyyyMMddHHmmssZ";
                    break;
                case 15:
                    mask = "yyyyMMddHHmmssZ"; // GeneralizedTime
                    break;
            }
            return DateTime.ParseExact(t, mask, null);
        }
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class ASN1
    {

        private byte m_nTag;
        private byte[] m_aValue;
        private ArrayList elist;

        public ASN1() : this(0x00, null) { }

        public ASN1(byte tag) : this(tag, null) { }

        public ASN1(byte tag, byte[] data)
        {
            m_nTag = tag;
            m_aValue = data;
        }

        public ASN1(byte[] data)
        {
            m_nTag = data[0];

            int nLenLength = 0;
            int nLength = data[1];

            if (nLength > 0x80)
            {
                // composed length
                nLenLength = nLength - 0x80;
                nLength = 0;
                for (int i = 0; i < nLenLength; i++)
                {
                    nLength *= 256;
                    nLength += data[i + 2];
                }
            }
            else if (nLength == 0x80)
            {
                // undefined length encoding
                throw new NotSupportedException("Undefined length encoding.");
            }

            m_aValue = new byte[nLength];
            Buffer.BlockCopy(data, (2 + nLenLength), m_aValue, 0, nLength);

            if ((m_nTag & 0x20) == 0x20)
            {
                int nStart = (2 + nLenLength);
                Decode(data, ref nStart, data.Length);
            }
        }

        public int Count
        {
            get
            {
                if (elist == null)
                    return 0;
                return elist.Count;
            }
        }

        public byte Tag
        {
            get { return m_nTag; }
        }

        public int Length
        {
            get
            {
                if (m_aValue != null)
                    return m_aValue.Length;
                else
                    return 0;
            }
        }

        public byte[] Value
        {
            get
            {
                if (m_aValue == null)
                    GetBytes();
                return (byte[])m_aValue.Clone();
            }
            set
            {
                if (value != null)
                    m_aValue = (byte[])value.Clone();
            }
        }

        private bool CompareArray(byte[] array1, byte[] array2)
        {
            bool bResult = (array1.Length == array2.Length);
            if (bResult)
            {
                for (int i = 0; i < array1.Length; i++)
                {
                    if (array1[i] != array2[i])
                        return false;
                }
            }
            return bResult;
        }

        public bool Equals(byte[] asn1)
        {
            return CompareArray(this.GetBytes(), asn1);
        }

        public bool CompareValue(byte[] value)
        {
            return CompareArray(m_aValue, value);
        }

        public ASN1 Add(ASN1 asn1)
        {
            if (asn1 != null)
            {
                if (elist == null)
                    elist = new ArrayList();
                elist.Add(asn1);
            }
            return asn1;
        }

        public virtual byte[] GetBytes()
        {
            byte[] val = null;

            if (Count > 0)
            {
                int esize = 0;
                ArrayList al = new ArrayList();
                foreach (ASN1 a in elist)
                {
                    byte[] item = a.GetBytes();
                    al.Add(item);
                    esize += item.Length;
                }
                val = new byte[esize];
                int pos = 0;
                for (int i = 0; i < elist.Count; i++)
                {
                    byte[] item = (byte[])al[i];
                    Buffer.BlockCopy(item, 0, val, pos, item.Length);
                    pos += item.Length;
                }
            }
            else if (m_aValue != null)
            {
                val = m_aValue;
            }

            byte[] der;
            int nLengthLen = 0;

            if (val != null)
            {
                int nLength = val.Length;
                // special for length > 127
                if (nLength > 127)
                {
                    if (nLength <= Byte.MaxValue)
                    {
                        der = new byte[3 + nLength];
                        Buffer.BlockCopy(val, 0, der, 3, nLength);
                        nLengthLen = 0x81;
                        der[2] = (byte)(nLength);
                    }
                    else if (nLength <= UInt16.MaxValue)
                    {
                        der = new byte[4 + nLength];
                        Buffer.BlockCopy(val, 0, der, 4, nLength);
                        nLengthLen = 0x82;
                        der[2] = (byte)(nLength >> 8);
                        der[3] = (byte)(nLength);
                    }
                    else if (nLength <= 0xFFFFFF)
                    {
                        // 24 bits
                        der = new byte[5 + nLength];
                        Buffer.BlockCopy(val, 0, der, 5, nLength);
                        nLengthLen = 0x83;
                        der[2] = (byte)(nLength >> 16);
                        der[3] = (byte)(nLength >> 8);
                        der[4] = (byte)(nLength);
                    }
                    else
                    {
                        // max (Length is an integer) 32 bits
                        der = new byte[6 + nLength];
                        Buffer.BlockCopy(val, 0, der, 6, nLength);
                        nLengthLen = 0x84;
                        der[2] = (byte)(nLength >> 24);
                        der[3] = (byte)(nLength >> 16);
                        der[4] = (byte)(nLength >> 8);
                        der[5] = (byte)(nLength);
                    }
                }
                else
                {
                    // basic case (no encoding)
                    der = new byte[2 + nLength];
                    Buffer.BlockCopy(val, 0, der, 2, nLength);
                    nLengthLen = nLength;
                }
                if (m_aValue == null)
                    m_aValue = val;
            }
            else
                der = new byte[2];

            der[0] = m_nTag;
            der[1] = (byte)nLengthLen;

            return der;
        }

        // Note: Recursive
        protected void Decode(byte[] asn1, ref int anPos, int anLength)
        {
            byte nTag;
            int nLength;
            byte[] aValue;

            // minimum is 2 bytes (tag + length of 0)
            while (anPos < anLength - 1)
            {
                DecodeTLV(asn1, ref anPos, out nTag, out nLength, out aValue);
                // sometimes we get trailing 0
                if (nTag == 0)
                    continue;

                ASN1 elm = Add(new ASN1(nTag, aValue));

                if ((nTag & 0x20) == 0x20)
                {
                    int nConstructedPos = anPos;
                    elm.Decode(asn1, ref nConstructedPos, nConstructedPos + nLength);
                }
                anPos += nLength; // value length
            }
        }

        // TLV : Tag - Length - Value
        protected void DecodeTLV(byte[] asn1, ref int pos, out byte tag, out int length, out byte[] content)
        {
            tag = asn1[pos++];
            length = asn1[pos++];

            // special case where L contains the Length of the Length + 0x80
            if ((length & 0x80) == 0x80)
            {
                int nLengthLen = length & 0x7F;
                length = 0;
                for (int i = 0; i < nLengthLen; i++)
                    length = length * 256 + asn1[pos++];
            }

            content = new byte[length];
            Buffer.BlockCopy(asn1, pos, content, 0, length);
        }

        public ASN1 this[int index]
        {
            get
            {
                try
                {
                    if ((elist == null) || (index >= elist.Count))
                        return null;
                    return (ASN1)elist[index];
                }
                catch (ArgumentOutOfRangeException)
                {
                    return null;
                }
            }
        }

        public ASN1 Element(int index, byte anTag)
        {
            try
            {
                if ((elist == null) || (index >= elist.Count))
                    return null;

                ASN1 elm = (ASN1)elist[index];
                if (elm.Tag == anTag)
                    return elm;
                else
                    return null;
            }
            catch (ArgumentOutOfRangeException)
            {
                return null;
            }
        }

        public override string ToString()
        {
            StringBuilder hexLine = new StringBuilder();

            // Add tag
            hexLine.AppendFormat("Tag: {0} {1}", m_nTag.ToString("X2"), Environment.NewLine);

            // Add length
            hexLine.AppendFormat("Length: {0} {1}", Value.Length, Environment.NewLine);

            // Add value
            hexLine.Append("Value: ");
            hexLine.Append(Environment.NewLine);
            for (int i = 0; i < Value.Length; i++)
            {
                hexLine.AppendFormat("{0} ", Value[i].ToString("X2"));
                if ((i + 1) % 16 == 0)
                    hexLine.AppendFormat(Environment.NewLine);
            }
            return hexLine.ToString();
        }

        public void SaveToFile(string filename)
        {
            if (filename == null)
                throw new ArgumentNullException("filename");

            using (FileStream fs = File.OpenWrite(filename))
            {
                byte[] data = GetBytes();
                fs.Write(data, 0, data.Length);
                fs.Flush();
                fs.Close();
            }
        }
    }

    public interface ITrustAnchors
    {
        X509CertificateCollection Anchors { get; }
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class TrustAnchors : ITrustAnchors
    {

        static byte[] msroot = { 
			0x30, 0x82, 0x04, 0x12, 0x30, 0x82, 0x02, 0xFA, 0xA0, 0x03, 0x02, 0x01, 
			0x02, 0x02, 0x0F, 0x00, 0xC1, 0x00, 0x8B, 0x3C, 0x3C, 0x88, 0x11, 0xD1, 
			0x3E, 0xF6, 0x63, 0xEC, 0xDF, 0x40, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 
			0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x05, 0x00, 0x30, 0x70, 0x31, 
			0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 0x43, 0x6F, 
			0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 
			0x31, 0x39, 0x39, 0x37, 0x20, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 
			0x66, 0x74, 0x20, 0x43, 0x6F, 0x72, 0x70, 0x2E, 0x31, 0x1E, 0x30, 0x1C, 
			0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x15, 0x4D, 0x69, 0x63, 0x72, 0x6F, 
			0x73, 0x6F, 0x66, 0x74, 0x20, 0x43, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 
			0x74, 0x69, 0x6F, 0x6E, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x04, 
			0x03, 0x13, 0x18, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 
			0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 
			0x69, 0x74, 0x79, 0x30, 0x1E, 0x17, 0x0D, 0x39, 0x37, 0x30, 0x31, 0x31, 
			0x30, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x17, 0x0D, 0x32, 0x30, 
			0x31, 0x32, 0x33, 0x31, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 
			0x70, 0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 
			0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 
			0x29, 0x20, 0x31, 0x39, 0x39, 0x37, 0x20, 0x4D, 0x69, 0x63, 0x72, 0x6F, 
			0x73, 0x6F, 0x66, 0x74, 0x20, 0x43, 0x6F, 0x72, 0x70, 0x2E, 0x31, 0x1E, 
			0x30, 0x1C, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x15, 0x4D, 0x69, 0x63, 
			0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x43, 0x6F, 0x72, 0x70, 0x6F, 
			0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 
			0x55, 0x04, 0x03, 0x13, 0x18, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 
			0x66, 0x74, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 
			0x6F, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 
			0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 
			0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 
			0x01, 0x00, 0xA9, 0x02, 0xBD, 0xC1, 0x70, 0xE6, 0x3B, 0xF2, 0x4E, 0x1B, 
			0x28, 0x9F, 0x97, 0x78, 0x5E, 0x30, 0xEA, 0xA2, 0xA9, 0x8D, 0x25, 0x5F, 
			0xF8, 0xFE, 0x95, 0x4C, 0xA3, 0xB7, 0xFE, 0x9D, 0xA2, 0x20, 0x3E, 0x7C, 
			0x51, 0xA2, 0x9B, 0xA2, 0x8F, 0x60, 0x32, 0x6B, 0xD1, 0x42, 0x64, 0x79, 
			0xEE, 0xAC, 0x76, 0xC9, 0x54, 0xDA, 0xF2, 0xEB, 0x9C, 0x86, 0x1C, 0x8F, 
			0x9F, 0x84, 0x66, 0xB3, 0xC5, 0x6B, 0x7A, 0x62, 0x23, 0xD6, 0x1D, 0x3C, 
			0xDE, 0x0F, 0x01, 0x92, 0xE8, 0x96, 0xC4, 0xBF, 0x2D, 0x66, 0x9A, 0x9A, 
			0x68, 0x26, 0x99, 0xD0, 0x3A, 0x2C, 0xBF, 0x0C, 0xB5, 0x58, 0x26, 0xC1, 
			0x46, 0xE7, 0x0A, 0x3E, 0x38, 0x96, 0x2C, 0xA9, 0x28, 0x39, 0xA8, 0xEC, 
			0x49, 0x83, 0x42, 0xE3, 0x84, 0x0F, 0xBB, 0x9A, 0x6C, 0x55, 0x61, 0xAC, 
			0x82, 0x7C, 0xA1, 0x60, 0x2D, 0x77, 0x4C, 0xE9, 0x99, 0xB4, 0x64, 0x3B, 
			0x9A, 0x50, 0x1C, 0x31, 0x08, 0x24, 0x14, 0x9F, 0xA9, 0xE7, 0x91, 0x2B, 
			0x18, 0xE6, 0x3D, 0x98, 0x63, 0x14, 0x60, 0x58, 0x05, 0x65, 0x9F, 0x1D, 
			0x37, 0x52, 0x87, 0xF7, 0xA7, 0xEF, 0x94, 0x02, 0xC6, 0x1B, 0xD3, 0xBF, 
			0x55, 0x45, 0xB3, 0x89, 0x80, 0xBF, 0x3A, 0xEC, 0x54, 0x94, 0x4E, 0xAE, 
			0xFD, 0xA7, 0x7A, 0x6D, 0x74, 0x4E, 0xAF, 0x18, 0xCC, 0x96, 0x09, 0x28, 
			0x21, 0x00, 0x57, 0x90, 0x60, 0x69, 0x37, 0xBB, 0x4B, 0x12, 0x07, 0x3C, 
			0x56, 0xFF, 0x5B, 0xFB, 0xA4, 0x66, 0x0A, 0x08, 0xA6, 0xD2, 0x81, 0x56, 
			0x57, 0xEF, 0xB6, 0x3B, 0x5E, 0x16, 0x81, 0x77, 0x04, 0xDA, 0xF6, 0xBE, 
			0xAE, 0x80, 0x95, 0xFE, 0xB0, 0xCD, 0x7F, 0xD6, 0xA7, 0x1A, 0x72, 0x5C, 
			0x3C, 0xCA, 0xBC, 0xF0, 0x08, 0xA3, 0x22, 0x30, 0xB3, 0x06, 0x85, 0xC9, 
			0xB3, 0x20, 0x77, 0x13, 0x85, 0xDF, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 
			0x81, 0xA8, 0x30, 0x81, 0xA5, 0x30, 0x81, 0xA2, 0x06, 0x03, 0x55, 0x1D, 
			0x01, 0x04, 0x81, 0x9A, 0x30, 0x81, 0x97, 0x80, 0x10, 0x5B, 0xD0, 0x70, 
			0xEF, 0x69, 0x72, 0x9E, 0x23, 0x51, 0x7E, 0x14, 0xB2, 0x4D, 0x8E, 0xFF, 
			0xCB, 0xA1, 0x72, 0x30, 0x70, 0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 
			0x04, 0x0B, 0x13, 0x22, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 
			0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x37, 0x20, 0x4D, 
			0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x43, 0x6F, 0x72, 
			0x70, 0x2E, 0x31, 0x1E, 0x30, 0x1C, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 
			0x15, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x43, 
			0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x31, 0x21, 
			0x30, 0x1F, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x4D, 0x69, 0x63, 
			0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 
			0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x82, 0x0F, 0x00, 
			0xC1, 0x00, 0x8B, 0x3C, 0x3C, 0x88, 0x11, 0xD1, 0x3E, 0xF6, 0x63, 0xEC, 
			0xDF, 0x40, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 
			0x01, 0x01, 0x04, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x95, 0xE8, 
			0x0B, 0xC0, 0x8D, 0xF3, 0x97, 0x18, 0x35, 0xED, 0xB8, 0x01, 0x24, 0xD8, 
			0x77, 0x11, 0xF3, 0x5C, 0x60, 0x32, 0x9F, 0x9E, 0x0B, 0xCB, 0x3E, 0x05, 
			0x91, 0x88, 0x8F, 0xC9, 0x3A, 0xE6, 0x21, 0xF2, 0xF0, 0x57, 0x93, 0x2C, 
			0xB5, 0xA0, 0x47, 0xC8, 0x62, 0xEF, 0xFC, 0xD7, 0xCC, 0x3B, 0x3B, 0x5A, 
			0xA9, 0x36, 0x54, 0x69, 0xFE, 0x24, 0x6D, 0x3F, 0xC9, 0xCC, 0xAA, 0xDE, 
			0x05, 0x7C, 0xDD, 0x31, 0x8D, 0x3D, 0x9F, 0x10, 0x70, 0x6A, 0xBB, 0xFE, 
			0x12, 0x4F, 0x18, 0x69, 0xC0, 0xFC, 0xD0, 0x43, 0xE3, 0x11, 0x5A, 0x20, 
			0x4F, 0xEA, 0x62, 0x7B, 0xAF, 0xAA, 0x19, 0xC8, 0x2B, 0x37, 0x25, 0x2D, 
			0xBE, 0x65, 0xA1, 0x12, 0x8A, 0x25, 0x0F, 0x63, 0xA3, 0xF7, 0x54, 0x1C, 
			0xF9, 0x21, 0xC9, 0xD6, 0x15, 0xF3, 0x52, 0xAC, 0x6E, 0x43, 0x32, 0x07, 
			0xFD, 0x82, 0x17, 0xF8, 0xE5, 0x67, 0x6C, 0x0D, 0x51, 0xF6, 0xBD, 0xF1, 
			0x52, 0xC7, 0xBD, 0xE7, 0xC4, 0x30, 0xFC, 0x20, 0x31, 0x09, 0x88, 0x1D, 
			0x95, 0x29, 0x1A, 0x4D, 0xD5, 0x1D, 0x02, 0xA5, 0xF1, 0x80, 0xE0, 0x03, 
			0xB4, 0x5B, 0xF4, 0xB1, 0xDD, 0xC8, 0x57, 0xEE, 0x65, 0x49, 0xC7, 0x52, 
			0x54, 0xB6, 0xB4, 0x03, 0x28, 0x12, 0xFF, 0x90, 0xD6, 0xF0, 0x08, 0x8F, 
			0x7E, 0xB8, 0x97, 0xC5, 0xAB, 0x37, 0x2C, 0xE4, 0x7A, 0xE4, 0xA8, 0x77, 
			0xE3, 0x76, 0xA0, 0x00, 0xD0, 0x6A, 0x3F, 0xC1, 0xD2, 0x36, 0x8A, 0xE0, 
			0x41, 0x12, 0xA8, 0x35, 0x6A, 0x1B, 0x6A, 0xDB, 0x35, 0xE1, 0xD4, 0x1C, 
			0x04, 0xE4, 0xA8, 0x45, 0x04, 0xC8, 0x5A, 0x33, 0x38, 0x6E, 0x4D, 0x1C, 
			0x0D, 0x62, 0xB7, 0x0A, 0xA2, 0x8C, 0xD3, 0xD5, 0x54, 0x3F, 0x46, 0xCD, 
			0x1C, 0x55, 0xA6, 0x70, 0xDB, 0x12, 0x3A, 0x87, 0x93, 0x75, 0x9F, 0xA7, 
			0xD2, 0xA0 };

        static byte[] verisign = { 
			0x30, 0x82, 0x02, 0x40, 0x30, 0x82, 0x01, 0xA9, 0x02, 0x10, 0x03, 0xC7, 
			0x8F, 0x37, 0xDB, 0x92, 0x28, 0xDF, 0x3C, 0xBB, 0x1A, 0xAD, 0x82, 0xFA, 
			0x67, 0x10, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 
			0x01, 0x01, 0x02, 0x05, 0x00, 0x30, 0x61, 0x31, 0x11, 0x30, 0x0F, 0x06, 
			0x03, 0x55, 0x04, 0x07, 0x13, 0x08, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 
			0x65, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 
			0x0E, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 0x49, 
			0x6E, 0x63, 0x2E, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0B, 
			0x13, 0x2A, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 
			0x6F, 0x6D, 0x6D, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6C, 0x20, 0x53, 0x6F, 
			0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 
			0x73, 0x68, 0x65, 0x72, 0x73, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, 
			0x39, 0x36, 0x30, 0x34, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 
			0x5A, 0x17, 0x0D, 0x30, 0x34, 0x30, 0x31, 0x30, 0x37, 0x32, 0x33, 0x35, 
			0x39, 0x35, 0x39, 0x5A, 0x30, 0x61, 0x31, 0x11, 0x30, 0x0F, 0x06, 0x03, 
			0x55, 0x04, 0x07, 0x13, 0x08, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 
			0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0E, 
			0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 0x49, 0x6E, 
			0x63, 0x2E, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 
			0x2A, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x6F, 
			0x6D, 0x6D, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6C, 0x20, 0x53, 0x6F, 0x66, 
			0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x73, 
			0x68, 0x65, 0x72, 0x73, 0x20, 0x43, 0x41, 0x30, 0x81, 0x9F, 0x30, 0x0D, 
			0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 
			0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 
			0xC3, 0xD3, 0x69, 0x65, 0x52, 0x01, 0x94, 0x54, 0xAB, 0x28, 0xC6, 0x62, 
			0x18, 0xB3, 0x54, 0x55, 0xC5, 0x44, 0x87, 0x45, 0x4A, 0x3B, 0xC2, 0x7E, 
			0xD8, 0xD3, 0xD7, 0xC8, 0x80, 0x86, 0x8D, 0xD8, 0x0C, 0xF1, 0x16, 0x9C, 
			0xCC, 0x6B, 0xA9, 0x29, 0xB2, 0x8F, 0x76, 0x73, 0x92, 0xC8, 0xC5, 0x62, 
			0xA6, 0x3C, 0xED, 0x1E, 0x05, 0x75, 0xF0, 0x13, 0x00, 0x6C, 0x14, 0x4D, 
			0xD4, 0x98, 0x90, 0x07, 0xBE, 0x69, 0x73, 0x81, 0xB8, 0x62, 0x4E, 0x31, 
			0x1E, 0xD1, 0xFC, 0xC9, 0x0C, 0xEB, 0x7D, 0x90, 0xBF, 0xAE, 0xB4, 0x47, 
			0x51, 0xEC, 0x6F, 0xCE, 0x64, 0x35, 0x02, 0xD6, 0x7D, 0x67, 0x05, 0x77, 
			0xE2, 0x8F, 0xD9, 0x51, 0xD7, 0xFB, 0x97, 0x19, 0xBC, 0x3E, 0xD7, 0x77, 
			0x81, 0xC6, 0x43, 0xDD, 0xF2, 0xDD, 0xDF, 0xCA, 0xA3, 0x83, 0x8B, 0xCB, 
			0x41, 0xC1, 0x3D, 0x22, 0x48, 0x48, 0xA6, 0x19, 0x02, 0x03, 0x01, 0x00, 
			0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 
			0x01, 0x02, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0xB5, 0xBC, 0xB0, 0x75, 
			0x6A, 0x89, 0xA2, 0x86, 0xBD, 0x64, 0x78, 0xC3, 0xA7, 0x32, 0x75, 0x72, 
			0x11, 0xAA, 0x26, 0x02, 0x17, 0x60, 0x30, 0x4C, 0xE3, 0x48, 0x34, 0x19, 
			0xB9, 0x52, 0x4A, 0x51, 0x18, 0x80, 0xFE, 0x53, 0x2D, 0x7B, 0xD5, 0x31, 
			0x8C, 0xC5, 0x65, 0x99, 0x41, 0x41, 0x2F, 0xF2, 0xAE, 0x63, 0x7A, 0xE8, 
			0x73, 0x99, 0x15, 0x90, 0x1A, 0x1F, 0x7A, 0x8B, 0x41, 0xD0, 0x8E, 0x3A, 
			0xD0, 0xCD, 0x38, 0x34, 0x44, 0xD0, 0x75, 0xF8, 0xEA, 0x71, 0xC4, 0x81, 
			0x19, 0x38, 0x17, 0x35, 0x4A, 0xAE, 0xC5, 0x3E, 0x32, 0xE6, 0x21, 0xB8, 
			0x05, 0xC0, 0x93, 0xE1, 0xC7, 0x38, 0x5C, 0xD8, 0xF7, 0x93, 0x38, 0x64, 
			0x90, 0xED, 0x54, 0xCE, 0xCA, 0xD3, 0xD3, 0xD0, 0x5F, 0xEF, 0x04, 0x9B, 
			0xDE, 0x02, 0x82, 0xDD, 0x88, 0x29, 0xB1, 0xC3, 0x4F, 0xA5, 0xCD, 0x71, 
			0x64, 0x31, 0x3C, 0x3C };

        static byte[] verisign_ts_root = { 
			0x30, 0x82, 0x02, 0xBC, 0x30, 0x82, 0x02, 0x25, 0x02, 0x10, 0x4A, 0x19, 
			0xD2, 0x38, 0x8C, 0x82, 0x59, 0x1C, 0xA5, 0x5D, 0x73, 0x5F, 0x15, 0x5D, 
			0xDC, 0xA3, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 
			0x01, 0x01, 0x04, 0x05, 0x00, 0x30, 0x81, 0x9E, 0x31, 0x1F, 0x30, 0x1D, 
			0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 
			0x69, 0x67, 0x6E, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4E, 0x65, 
			0x74, 0x77, 0x6F, 0x72, 0x6B, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 
			0x04, 0x0B, 0x13, 0x0E, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 
			0x2C, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 
			0x55, 0x04, 0x0B, 0x13, 0x23, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 
			0x6E, 0x20, 0x54, 0x69, 0x6D, 0x65, 0x20, 0x53, 0x74, 0x61, 0x6D, 0x70, 
			0x69, 0x6E, 0x67, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 
			0x52, 0x6F, 0x6F, 0x74, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 0x04, 
			0x0B, 0x13, 0x2B, 0x4E, 0x4F, 0x20, 0x4C, 0x49, 0x41, 0x42, 0x49, 0x4C, 
			0x49, 0x54, 0x59, 0x20, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 
			0x2C, 0x20, 0x28, 0x63, 0x29, 0x39, 0x37, 0x20, 0x56, 0x65, 0x72, 0x69, 
			0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x30, 0x1E, 
			0x17, 0x0D, 0x39, 0x37, 0x30, 0x35, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 
			0x30, 0x30, 0x5A, 0x17, 0x0D, 0x30, 0x34, 0x30, 0x31, 0x30, 0x37, 0x32, 
			0x33, 0x35, 0x39, 0x35, 0x39, 0x5A, 0x30, 0x81, 0x9E, 0x31, 0x1F, 0x30, 
			0x1D, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 
			0x53, 0x69, 0x67, 0x6E, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4E, 
			0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 
			0x55, 0x04, 0x0B, 0x13, 0x0E, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 
			0x6E, 0x2C, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x31, 0x2C, 0x30, 0x2A, 0x06, 
			0x03, 0x55, 0x04, 0x0B, 0x13, 0x23, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 
			0x67, 0x6E, 0x20, 0x54, 0x69, 0x6D, 0x65, 0x20, 0x53, 0x74, 0x61, 0x6D, 
			0x70, 0x69, 0x6E, 0x67, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 
			0x20, 0x52, 0x6F, 0x6F, 0x74, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 
			0x04, 0x0B, 0x13, 0x2B, 0x4E, 0x4F, 0x20, 0x4C, 0x49, 0x41, 0x42, 0x49, 
			0x4C, 0x49, 0x54, 0x59, 0x20, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 
			0x44, 0x2C, 0x20, 0x28, 0x63, 0x29, 0x39, 0x37, 0x20, 0x56, 0x65, 0x72, 
			0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x30, 
			0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 
			0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 
			0x02, 0x81, 0x81, 0x00, 0xD3, 0x2E, 0x20, 0xF0, 0x68, 0x7C, 0x2C, 0x2D, 
			0x2E, 0x81, 0x1C, 0xB1, 0x06, 0xB2, 0xA7, 0x0B, 0xB7, 0x11, 0x0D, 0x57, 
			0xDA, 0x53, 0xD8, 0x75, 0xE3, 0xC9, 0x33, 0x2A, 0xB2, 0xD4, 0xF6, 0x09, 
			0x5B, 0x34, 0xF3, 0xE9, 0x90, 0xFE, 0x09, 0x0C, 0xD0, 0xDB, 0x1B, 0x5A, 
			0xB9, 0xCD, 0xE7, 0xF6, 0x88, 0xB1, 0x9D, 0xC0, 0x87, 0x25, 0xEB, 0x7D, 
			0x58, 0x10, 0x73, 0x6A, 0x78, 0xCB, 0x71, 0x15, 0xFD, 0xC6, 0x58, 0xF6, 
			0x29, 0xAB, 0x58, 0x5E, 0x96, 0x04, 0xFD, 0x2D, 0x62, 0x11, 0x58, 0x81, 
			0x1C, 0xCA, 0x71, 0x94, 0xD5, 0x22, 0x58, 0x2F, 0xD5, 0xCC, 0x14, 0x05, 
			0x84, 0x36, 0xBA, 0x94, 0xAA, 0xB4, 0x4D, 0x4A, 0xE9, 0xEE, 0x3B, 0x22, 
			0xAD, 0x56, 0x99, 0x7E, 0x21, 0x9C, 0x6C, 0x86, 0xC0, 0x4A, 0x47, 0x97, 
			0x6A, 0xB4, 0xA6, 0x36, 0xD5, 0xFC, 0x09, 0x2D, 0xD3, 0xB4, 0x39, 0x9B, 
			0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 
			0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 
			0x61, 0x55, 0x0E, 0x3E, 0x7B, 0xC7, 0x92, 0x12, 0x7E, 0x11, 0x10, 0x8E, 
			0x22, 0xCC, 0xD4, 0xB3, 0x13, 0x2B, 0x5B, 0xE8, 0x44, 0xE4, 0x0B, 0x78, 
			0x9E, 0xA4, 0x7E, 0xF3, 0xA7, 0x07, 0x72, 0x1E, 0xE2, 0x59, 0xEF, 0xCC, 
			0x84, 0xE3, 0x89, 0x94, 0x4C, 0xDB, 0x4E, 0x61, 0xEF, 0xB3, 0xA4, 0xFB, 
			0x46, 0x3D, 0x50, 0x34, 0x0B, 0x9F, 0x70, 0x56, 0xF6, 0x8E, 0x2A, 0x7F, 
			0x17, 0xCE, 0xE5, 0x63, 0xBF, 0x79, 0x69, 0x07, 0x73, 0x2E, 0xB0, 0x95, 
			0x28, 0x8A, 0xF5, 0xED, 0xAA, 0xA9, 0xD2, 0x5D, 0xCD, 0x0A, 0xCA, 0x10, 
			0x09, 0x8F, 0xCE, 0xB3, 0xAF, 0x28, 0x96, 0xC4, 0x79, 0x29, 0x84, 0x92, 
			0xDC, 0xFF, 0xBA, 0x67, 0x42, 0x48, 0xA6, 0x90, 0x10, 0xE4, 0xBF, 0x61, 
			0xF8, 0x9C, 0x53, 0xE5, 0x93, 0xD1, 0x73, 0x3F, 0xF8, 0xFD, 0x9D, 0x4F, 
			0x84, 0xAC, 0x55, 0xD1, 0xFD, 0x11, 0x63, 0x63 };

        // old verisign code signing certificate (96-99) using MD2
        // still valid because of the timestamps
        static byte[] oldverisign = { 
			0x30, 0x82, 0x02, 0x35, 0x30, 0x82, 0x01, 0x9E, 0x02, 0x05, 0x02, 0xB4, 
			0x00, 0x00, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 
			0x0D, 0x01, 0x01, 0x02, 0x05, 0x00, 0x30, 0x61, 0x31, 0x11, 0x30, 0x0F, 
			0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x08, 0x49, 0x6E, 0x74, 0x65, 0x72, 
			0x6E, 0x65, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0A, 
			0x13, 0x0E, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 
			0x49, 0x6E, 0x63, 0x2E, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 
			0x0B, 0x13, 0x2A, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 
			0x43, 0x6F, 0x6D, 0x6D, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6C, 0x20, 0x53, 
			0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x50, 0x75, 0x62, 0x6C, 
			0x69, 0x73, 0x68, 0x65, 0x72, 0x73, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 
			0x0D, 0x39, 0x36, 0x30, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, 0x35, 0x35, 
			0x39, 0x5A, 0x17, 0x0D, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x30, 0x39, 
			0x33, 0x35, 0x35, 0x38, 0x5A, 0x30, 0x61, 0x31, 0x11, 0x30, 0x0F, 0x06, 
			0x03, 0x55, 0x04, 0x07, 0x13, 0x08, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 
			0x65, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 
			0x0E, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x2C, 0x20, 0x49, 
			0x6E, 0x63, 0x2E, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0B, 
			0x13, 0x2A, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 
			0x6F, 0x6D, 0x6D, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6C, 0x20, 0x53, 0x6F, 
			0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 
			0x73, 0x68, 0x65, 0x72, 0x73, 0x20, 0x43, 0x41, 0x30, 0x81, 0x9F, 0x30, 
			0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 
			0x05, 0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 
			0x00, 0xC3, 0xD3, 0x69, 0x65, 0x52, 0x01, 0x94, 0x54, 0xAB, 0x28, 0xC6, 
			0x62, 0x18, 0xB3, 0x54, 0x55, 0xC5, 0x44, 0x87, 0x45, 0x4A, 0x3B, 0xC2, 
			0x7E, 0xD8, 0xD3, 0xD7, 0xC8, 0x80, 0x86, 0x8D, 0xD8, 0x0C, 0xF1, 0x16, 
			0x9C, 0xCC, 0x6B, 0xA9, 0x29, 0xB2, 0x8F, 0x76, 0x73, 0x92, 0xC8, 0xC5, 
			0x62, 0xA6, 0x3C, 0xED, 0x1E, 0x05, 0x75, 0xF0, 0x13, 0x00, 0x6C, 0x14, 
			0x4D, 0xD4, 0x98, 0x90, 0x07, 0xBE, 0x69, 0x73, 0x81, 0xB8, 0x62, 0x4E, 
			0x31, 0x1E, 0xD1, 0xFC, 0xC9, 0x0C, 0xEB, 0x7D, 0x90, 0xBF, 0xAE, 0xB4, 
			0x47, 0x51, 0xEC, 0x6F, 0xCE, 0x64, 0x35, 0x02, 0xD6, 0x7D, 0x67, 0x05, 
			0x77, 0xE2, 0x8F, 0xD9, 0x51, 0xD7, 0xFB, 0x97, 0x19, 0xBC, 0x3E, 0xD7, 
			0x77, 0x81, 0xC6, 0x43, 0xDD, 0xF2, 0xDD, 0xDF, 0xCA, 0xA3, 0x83, 0x8B, 
			0xCB, 0x41, 0xC1, 0x3D, 0x22, 0x48, 0x48, 0xA6, 0x19, 0x02, 0x03, 0x01, 
			0x00, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 
			0x01, 0x01, 0x02, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x31, 0xBB, 0x30, 
			0xC5, 0x6F, 0xA7, 0xBE, 0x23, 0x26, 0x6D, 0xA5, 0x99, 0x76, 0x68, 0xC5, 
			0x2A, 0x03, 0x28, 0x4B, 0xF3, 0x89, 0xB0, 0x99, 0x03, 0x32, 0x5B, 0x94, 
			0xA1, 0x7B, 0xC1, 0xC8, 0x19, 0xD7, 0xF4, 0x95, 0x6C, 0xAC, 0x73, 0x24, 
			0x0A, 0xCB, 0x44, 0x05, 0x7D, 0x78, 0xEE, 0xFA, 0xF6, 0xA7, 0x9F, 0x87, 
			0xA4, 0x7F, 0xE8, 0xF3, 0x4B, 0x4F, 0x32, 0x30, 0x30, 0x15, 0x08, 0x17, 
			0x01, 0xB2, 0x80, 0xFC, 0xA1, 0xD9, 0x24, 0x87, 0xA5, 0x00, 0x5F, 0xCD, 
			0xDD, 0x29, 0xC8, 0xA1, 0xA5, 0xCA, 0x58, 0x75, 0x39, 0x60, 0x45, 0x1F, 
			0xDE, 0x8D, 0xD6, 0x57, 0x08, 0xD3, 0xC0, 0x1B, 0x81, 0xC2, 0xD9, 0xE2, 
			0x00, 0x8C, 0xEC, 0x0A, 0x91, 0x02, 0xC6, 0x9D, 0x36, 0x74, 0x9A, 0x83, 
			0x6B, 0xEF, 0x7C, 0x8C, 0xD2, 0xA5, 0x2A, 0x6A, 0xC9, 0x7E, 0xDB, 0xA9, 
			0xBD, 0x2B, 0x22, 0xFF, 0x1C };

        static byte[] thawte = { 
			0x30, 0x82, 0x03, 0x13, 0x30, 0x82, 0x02, 0x7C, 0xA0, 0x03, 0x02, 0x01, 
			0x02, 0x02, 0x01, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 
			0xF7, 0x0D, 0x01, 0x01, 0x04, 0x05, 0x00, 0x30, 0x81, 0xC4, 0x31, 0x0B, 
			0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x5A, 0x41, 0x31, 
			0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0C, 0x57, 0x65, 
			0x73, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x43, 0x61, 0x70, 0x65, 0x31, 0x12, 
			0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x61, 0x70, 
			0x65, 0x20, 0x54, 0x6F, 0x77, 0x6E, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 
			0x55, 0x04, 0x0A, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 
			0x43, 0x6F, 0x6E, 0x73, 0x75, 0x6C, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x63, 
			0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x1F, 
			0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 
			0x6E, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44, 
			0x69, 0x76, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17, 0x06, 
			0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 
			0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x26, 
			0x30, 0x24, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 
			0x01, 0x16, 0x17, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2D, 0x63, 0x65, 
			0x72, 0x74, 0x73, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2E, 0x63, 
			0x6F, 0x6D, 0x30, 0x1E, 0x17, 0x0D, 0x39, 0x36, 0x30, 0x38, 0x30, 0x31, 
			0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x17, 0x0D, 0x32, 0x30, 0x31, 
			0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5A, 0x30, 0x81, 
			0xC4, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 
			0x5A, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 
			0x0C, 0x57, 0x65, 0x73, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x43, 0x61, 0x70, 
			0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 
			0x43, 0x61, 0x70, 0x65, 0x20, 0x54, 0x6F, 0x77, 0x6E, 0x31, 0x1D, 0x30, 
			0x1B, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77, 
			0x74, 0x65, 0x20, 0x43, 0x6F, 0x6E, 0x73, 0x75, 0x6C, 0x74, 0x69, 0x6E, 
			0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 
			0x0B, 0x13, 0x1F, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 
			0x74, 0x69, 0x6F, 0x6E, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 
			0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x31, 0x19, 
			0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x54, 0x68, 0x61, 
			0x77, 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 
			0x41, 0x31, 0x26, 0x30, 0x24, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 
			0x0D, 0x01, 0x09, 0x01, 0x16, 0x17, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 
			0x2D, 0x63, 0x65, 0x72, 0x74, 0x73, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 
			0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 
			0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 
			0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xD3, 0xA4, 
			0x50, 0x6E, 0xC8, 0xFF, 0x56, 0x6B, 0xE6, 0xCF, 0x5D, 0xB6, 0xEA, 0x0C, 
			0x68, 0x75, 0x47, 0xA2, 0xAA, 0xC2, 0xDA, 0x84, 0x25, 0xFC, 0xA8, 0xF4, 
			0x47, 0x51, 0xDA, 0x85, 0xB5, 0x20, 0x74, 0x94, 0x86, 0x1E, 0x0F, 0x75, 
			0xC9, 0xE9, 0x08, 0x61, 0xF5, 0x06, 0x6D, 0x30, 0x6E, 0x15, 0x19, 0x02, 
			0xE9, 0x52, 0xC0, 0x62, 0xDB, 0x4D, 0x99, 0x9E, 0xE2, 0x6A, 0x0C, 0x44, 
			0x38, 0xCD, 0xFE, 0xBE, 0xE3, 0x64, 0x09, 0x70, 0xC5, 0xFE, 0xB1, 0x6B, 
			0x29, 0xB6, 0x2F, 0x49, 0xC8, 0x3B, 0xD4, 0x27, 0x04, 0x25, 0x10, 0x97, 
			0x2F, 0xE7, 0x90, 0x6D, 0xC0, 0x28, 0x42, 0x99, 0xD7, 0x4C, 0x43, 0xDE, 
			0xC3, 0xF5, 0x21, 0x6D, 0x54, 0x9F, 0x5D, 0xC3, 0x58, 0xE1, 0xC0, 0xE4, 
			0xD9, 0x5B, 0xB0, 0xB8, 0xDC, 0xB4, 0x7B, 0xDF, 0x36, 0x3A, 0xC2, 0xB5, 
			0x66, 0x22, 0x12, 0xD6, 0x87, 0x0D, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 
			0x13, 0x30, 0x11, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01, 
			0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0D, 0x06, 0x09, 
			0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x05, 0x00, 0x03, 
			0x81, 0x81, 0x00, 0x07, 0xFA, 0x4C, 0x69, 0x5C, 0xFB, 0x95, 0xCC, 0x46, 
			0xEE, 0x85, 0x83, 0x4D, 0x21, 0x30, 0x8E, 0xCA, 0xD9, 0xA8, 0x6F, 0x49, 
			0x1A, 0xE6, 0xDA, 0x51, 0xE3, 0x60, 0x70, 0x6C, 0x84, 0x61, 0x11, 0xA1, 
			0x1A, 0xC8, 0x48, 0x3E, 0x59, 0x43, 0x7D, 0x4F, 0x95, 0x3D, 0xA1, 0x8B, 
			0xB7, 0x0B, 0x62, 0x98, 0x7A, 0x75, 0x8A, 0xDD, 0x88, 0x4E, 0x4E, 0x9E, 
			0x40, 0xDB, 0xA8, 0xCC, 0x32, 0x74, 0xB9, 0x6F, 0x0D, 0xC6, 0xE3, 0xB3, 
			0x44, 0x0B, 0xD9, 0x8A, 0x6F, 0x9A, 0x29, 0x9B, 0x99, 0x18, 0x28, 0x3B, 
			0xD1, 0xE3, 0x40, 0x28, 0x9A, 0x5A, 0x3C, 0xD5, 0xB5, 0xE7, 0x20, 0x1B, 
			0x8B, 0xCA, 0xA4, 0xAB, 0x8D, 0xE9, 0x51, 0xD9, 0xE2, 0x4C, 0x2C, 0x59, 
			0xA9, 0xDA, 0xB9, 0xB2, 0x75, 0x1B, 0xF6, 0x42, 0xF2, 0xEF, 0xC7, 0xF2, 
			0x18, 0xF9, 0x89, 0xBC, 0xA3, 0xFF, 0x8A, 0x23, 0x2E, 0x70, 0x47 };

        static internal X509CertificateCollection coll;

        static TrustAnchors()
        {
            coll = new X509CertificateCollection();
            coll.Add(new X509Certificate(msroot));
            coll.Add(new X509Certificate(verisign));
            coll.Add(new X509Certificate(verisign_ts_root));
            coll.Add(new X509Certificate(thawte));
        }

        public X509CertificateCollection Anchors
        {
            get { return coll; }
        }
    }

    // References:
    // 1.	Information technology - Open Systems Interconnection - The Directory: Models
    //	http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-X.501-200102-I
    // 2.	RFC2253: Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names
    //	http://www.ietf.org/rfc/rfc2253.txt

    /*
     * Name ::= CHOICE { RDNSequence }
     * 
     * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
     * 
     * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
     */
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 sealed class X501
    {

        static byte[] countryName = { 0x55, 0x04, 0x06 };
        static byte[] organizationName = { 0x55, 0x04, 0x0A };
        static byte[] organizationalUnitName = { 0x55, 0x04, 0x0B };
        static byte[] commonName = { 0x55, 0x04, 0x03 };
        static byte[] localityName = { 0x55, 0x04, 0x07 };
        static byte[] stateOrProvinceName = { 0x55, 0x04, 0x08 };
        static byte[] streetAddress = { 0x55, 0x04, 0x09 };
        //static byte[] serialNumber = { 0x55, 0x04, 0x05 };
        static byte[] domainComponent = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 };
        static byte[] userid = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01 };
        static byte[] email = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 };

        private X501()
        {
        }

        static public string ToString(ASN1 seq)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < seq.Count; i++)
            {
                ASN1 entry = seq[i];
                // multiple entries are valid
                for (int k = 0; k < entry.Count; k++)
                {
                    ASN1 pair = entry[k];
                    ASN1 s = pair[1];
                    if (s == null)
                        continue;

                    ASN1 poid = pair[0];
                    if (poid == null)
                        continue;

                    if (poid.CompareValue(countryName))
                        sb.Append("C=");
                    else if (poid.CompareValue(organizationName))
                        sb.Append("O=");
                    else if (poid.CompareValue(organizationalUnitName))
                        sb.Append("OU=");
                    else if (poid.CompareValue(commonName))
                        sb.Append("CN=");
                    else if (poid.CompareValue(localityName))
                        sb.Append("L=");
                    else if (poid.CompareValue(stateOrProvinceName))
                        sb.Append("S=");	// NOTE: RFC2253 uses ST=
                    else if (poid.CompareValue(streetAddress))
                        sb.Append("STREET=");
                    else if (poid.CompareValue(domainComponent))
                        sb.Append("DC=");
                    else if (poid.CompareValue(userid))
                        sb.Append("UID=");
                    else if (poid.CompareValue(email))
                        sb.Append("E=");	// NOTE: Not part of RFC2253
                    else
                    {
                        // unknown OID
                        sb.Append("OID.");	// NOTE: Not present as RFC2253
                        sb.Append(ASN1Convert.ToOid(poid));
                        sb.Append("=");
                    }

                    string sValue = null;
                    // 16bits or 8bits string ? TODO not complete (+special chars!)
                    if (s.Tag == 0x1E)
                    {
                        // BMPSTRING
                        StringBuilder sb2 = new StringBuilder();
                        for (int j = 1; j < s.Value.Length; j += 2)
                            sb2.Append((char)s.Value[j]);
                        sValue = sb2.ToString();
                    }
                    else
                    {
                        sValue = Encoding.UTF8.GetString(s.Value);
                        // in some cases we must quote (") the value
                        // Note: this doesn't seems to conform to RFC2253
                        char[] specials = { ',', '+', '"', '\\', '<', '>', ';' };
                        if (sValue.IndexOfAny(specials, 0, sValue.Length) > 0)
                            sValue = "\"" + sValue + "\"";
                        else if (sValue.StartsWith(" "))
                            sValue = "\"" + sValue + "\"";
                        else if (sValue.EndsWith(" "))
                            sValue = "\"" + sValue + "\"";
                    }

                    sb.Append(sValue);

                    // separator (not on last iteration)
                    if (k < entry.Count - 1)
                        sb.Append(", ");
                }

                // separator (not on last iteration)
                if (i < seq.Count - 1)
                    sb.Append(", ");
            }
            return sb.ToString();
        }

        static private X520.AttributeTypeAndValue GetAttributeFromOid(string attributeType)
        {
            string s = attributeType.ToUpper(CultureInfo.InvariantCulture).Trim();
            switch (s)
            {
                case "C":
                    return new X520.CountryName();
                case "O":
                    return new X520.OrganizationName();
                case "OU":
                    return new X520.OrganizationalUnitName();
                case "CN":
                    return new X520.CommonName();
                case "L":
                    return new X520.LocalityName();
                case "S":	// Microsoft
                case "ST":	// RFC2253
                    return new X520.StateOrProvinceName();
                case "E":	// NOTE: Not part of RFC2253
                    return new X520.EmailAddress();
                case "DC":	// RFC2247
                    return new X520.DomainComponent();
                case "UID":	// RFC1274
                    return new X520.UserId();
                default:
                    if (s == "OID.")
                    {
                        // MUST support it but it OID may be without it
                        return new X520.Oid(s.Substring(4));
                    }
                    else
                    {
                        if (IsOid(s))
                            return new X520.Oid(s);
                        else
                            return null;
                    }
            }
        }

        static private bool IsOid(string oid)
        {
            try
            {
                ASN1 asn = ASN1Convert.FromOid(oid);
                return (asn.Tag == 0x06);
            }
            catch
            {
                return false;
            }
        }

        // no quote processing
        static private X520.AttributeTypeAndValue ReadAttribute(string value, ref int pos)
        {
            while ((value[pos] == ' ') && (pos < value.Length))
                pos++;

            // get '=' position in substring
            int equal = value.IndexOf('=', pos);
            if (equal == -1)
            {
                string msg = ("No attribute found.");
                throw new FormatException(msg);
            }

            string s = value.Substring(pos, equal - pos);
            X520.AttributeTypeAndValue atv = GetAttributeFromOid(s);
            if (atv == null)
            {
                string msg = ("Unknown attribute '{0}'.");
                throw new FormatException(String.Format(msg, s));
            }
            pos = equal + 1; // skip the '='
            return atv;
        }

        static private bool IsHex(char c)
        {
            if (Char.IsDigit(c))
                return true;
            char up = Char.ToUpper(c, CultureInfo.InvariantCulture);
            return ((up >= 'A') && (up <= 'F'));
        }

        static string ReadHex(string value, ref int pos)
        {
            StringBuilder sb = new StringBuilder();
            // it is (at least an) 8 bits char
            sb.Append(value[pos++]);
            sb.Append(value[pos]);
            // look ahead for a 16 bits char
            if ((pos < value.Length - 4) && (value[pos + 1] == '\\') && IsHex(value[pos + 2]))
            {
                pos += 2; // pass last char and skip \
                sb.Append(value[pos++]);
                sb.Append(value[pos]);
            }
            byte[] data = CryptoConvert.FromHex(sb.ToString());
            return Encoding.UTF8.GetString(data);
        }

        static private int ReadEscaped(StringBuilder sb, string value, int pos)
        {
            switch (value[pos])
            {
                case '\\':
                case '"':
                case '=':
                case ';':
                case '<':
                case '>':
                case '+':
                case '#':
                case ',':
                    sb.Append(value[pos]);
                    return pos;
                default:
                    if (pos >= value.Length - 2)
                    {
                        string msg = ("Malformed escaped value '{0}'.");
                        throw new FormatException(string.Format(msg, value.Substring(pos)));
                    }
                    // it's either a 8 bits or 16 bits char
                    sb.Append(ReadHex(value, ref pos));
                    return pos;
            }
        }

        static private int ReadQuoted(StringBuilder sb, string value, int pos)
        {
            int original = pos;
            while (pos <= value.Length)
            {
                switch (value[pos])
                {
                    case '"':
                        return pos;
                    case '\\':
                        return ReadEscaped(sb, value, pos);
                    default:
                        sb.Append(value[pos]);
                        pos++;
                        break;
                }
            }
            string msg = ("Malformed quoted value '{0}'.");
            throw new FormatException(string.Format(msg, value.Substring(original)));
        }

        static private string ReadValue(string value, ref int pos)
        {
            int original = pos;
            StringBuilder sb = new StringBuilder();
            while (pos < value.Length)
            {
                switch (value[pos])
                {
                    case '\\':
                        pos = ReadEscaped(sb, value, ++pos);
                        break;
                    case '"':
                        pos = ReadQuoted(sb, value, ++pos);
                        break;
                    case '=':
                    case ';':
                    case '<':
                    case '>':
                        string msg = ("Malformed value '{0}' contains '{1}' outside quotes.");
                        throw new FormatException(string.Format(msg, value.Substring(original), value[pos]));
                    case '+':
                    case '#':
                        throw new NotImplementedException();
                    case ',':
                        pos++;
                        return sb.ToString();
                    default:
                        sb.Append(value[pos]);
                        break;
                }
                pos++;
            }
            return sb.ToString();
        }

        static public ASN1 FromString(string rdn)
        {
            if (rdn == null)
                throw new ArgumentNullException("rdn");

            int pos = 0;
            ASN1 asn1 = new ASN1(0x30);
            while (pos < rdn.Length)
            {
                X520.AttributeTypeAndValue atv = ReadAttribute(rdn, ref pos);
                atv.Value = ReadValue(rdn, ref pos);

                ASN1 sequence = new ASN1(0x31);
                sequence.Add(atv.GetASN1());
                asn1.Add(sequence);
            }
            return asn1;
        }
    }

    public abstract class X509Builder
    {

        private const string defaultHash = "SHA1";
        private string hashName;

        protected X509Builder()
        {
            hashName = defaultHash;
        }

        protected abstract ASN1 ToBeSigned(string hashName);

        // move to PKCS1
        protected string GetOid(string hashName)
        {
            switch (hashName.ToLower(CultureInfo.InvariantCulture))
            {
                case "md2":
                    // md2withRSAEncryption (1 2 840 113549 1 1 2)
                    return "1.2.840.113549.1.1.2";
                case "md4":
                    // md4withRSAEncryption (1 2 840 113549 1 1 3)
                    return "1.2.840.113549.1.1.3";
                case "md5":
                    // md5withRSAEncryption (1 2 840 113549 1 1 4)
                    return "1.2.840.113549.1.1.4";
                case "sha1":
                    // sha1withRSAEncryption (1 2 840 113549 1 1 5)
                    return "1.2.840.113549.1.1.5";
                case "sha256":
                    // sha256WithRSAEncryption 	OBJECT IDENTIFIER ::= { pkcs-1 11 }
                    return "1.2.840.113549.1.1.11";
                case "sha384":
                    // sha384WithRSAEncryption 	OBJECT IDENTIFIER ::= { pkcs-1 12 }
                    return "1.2.840.113549.1.1.12";
                case "sha512":
                    // sha512WithRSAEncryption 	OBJECT IDENTIFIER ::= { pkcs-1 13 }
                    return "1.2.840.113549.1.1.13";
                default:
                    throw new NotSupportedException("Unknown hash algorithm " + hashName);
            }
        }

        public string Hash
        {
            get { return hashName; }
            set
            {
                if (hashName == null)
                    hashName = defaultHash;
                else
                    hashName = value;
            }
        }

        public virtual byte[] Sign(AsymmetricAlgorithm aa)
        {
            if (aa is RSA)
                return Sign(aa as RSA);
            else if (aa is DSA)
                return Sign(aa as DSA);
            else
                throw new NotSupportedException("Unknown Asymmetric Algorithm " + aa.ToString());
        }

        private byte[] Build(ASN1 tbs, string hashoid, byte[] signature)
        {
            ASN1 builder = new ASN1(0x30);
            builder.Add(tbs);
            builder.Add(PKCS7.AlgorithmIdentifier(hashoid));
            // first byte of BITSTRING is the number of unused bits in the first byte
            byte[] bitstring = new byte[signature.Length + 1];
            Buffer.BlockCopy(signature, 0, bitstring, 1, signature.Length);
            builder.Add(new ASN1(0x03, bitstring));
            return builder.GetBytes();
        }

        public virtual byte[] Sign(RSA key)
        {
            string oid = GetOid(hashName);
            ASN1 tbs = ToBeSigned(oid);
            HashAlgorithm ha = HashAlgorithm.Create(hashName);
            byte[] hash = ha.ComputeHash(tbs.GetBytes());

            RSAPKCS1SignatureFormatter pkcs1 = new RSAPKCS1SignatureFormatter(key);
            pkcs1.SetHashAlgorithm(hashName);
            byte[] signature = pkcs1.CreateSignature(hash);

            return Build(tbs, oid, signature);
        }

        public virtual byte[] Sign(DSA key)
        {
            string oid = "1.2.840.10040.4.3";
            ASN1 tbs = ToBeSigned(oid);
            HashAlgorithm ha = HashAlgorithm.Create(hashName);
            if (!(ha is SHA1))
                throw new NotSupportedException("Only SHA-1 is supported for DSA");
            byte[] hash = ha.ComputeHash(tbs.GetBytes());

            DSASignatureFormatter dsa = new DSASignatureFormatter(key);
            dsa.SetHashAlgorithm(hashName);
            byte[] rs = dsa.CreateSignature(hash);

            // split R and S
            byte[] r = new byte[20];
            Buffer.BlockCopy(rs, 0, r, 0, 20);
            byte[] s = new byte[20];
            Buffer.BlockCopy(rs, 20, s, 0, 20);
            ASN1 signature = new ASN1(0x30);
            signature.Add(new ASN1(0x02, r));
            signature.Add(new ASN1(0x02, s));

            // dsaWithSha1 (1 2 840 10040 4 3)
            return Build(tbs, oid, signature.GetBytes());
        }
    }

#if INSIDE_CORLIB
	internal class X509Certificate : ISerializable {
#elif NET_2_0
	public class X509Certificate : ISerializable {
#else
    public class X509Certificate
    {
#endif

        private ASN1 decoder;

        private byte[] m_encodedcert;
        private DateTime m_from;
        private DateTime m_until;
        private string m_issuername;
        private string m_keyalgo;
        private byte[] m_keyalgoparams;
        private string m_subject;
        private byte[] m_publickey;
        private byte[] signature;
        private string m_signaturealgo;
        private byte[] m_signaturealgoparams;
        private byte[] certhash;
        private RSA _rsa;
        private DSA _dsa;

        // from http://www.ietf.org/rfc/rfc2459.txt
        //
        //Certificate  ::=  SEQUENCE  {
        //     tbsCertificate       TBSCertificate,
        //     signatureAlgorithm   AlgorithmIdentifier,
        //     signature            BIT STRING  }
        //
        //TBSCertificate  ::=  SEQUENCE  {
        //     version         [0]  Version DEFAULT v1,
        //     serialNumber         CertificateSerialNumber,
        //     signature            AlgorithmIdentifier,
        //     issuer               Name,
        //     validity             Validity,
        //     subject              Name,
        //     subjectPublicKeyInfo SubjectPublicKeyInfo,
        //     issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
        //                          -- If present, version shall be v2 or v3
        //     subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
        //                          -- If present, version shall be v2 or v3
        //     extensions      [3]  Extensions OPTIONAL
        //                          -- If present, version shall be v3 --  }
        private int version;
        private byte[] serialnumber;

        //		private byte[] issuerUniqueID;
        //		private byte[] subjectUniqueID;
        private X509ExtensionCollection extensions;

        // that's were the real job is!
        private void Parse(byte[] data)
        {
            string e = "Input data cannot be coded as a valid certificate.";
            try
            {
                decoder = new ASN1(data);
                // Certificate 
                if (decoder.Tag != 0x30)
                    throw new CryptographicException(e);
                // Certificate / TBSCertificate
                if (decoder[0].Tag != 0x30)
                    throw new CryptographicException(e);

                ASN1 tbsCertificate = decoder[0];

                int tbs = 0;
                // Certificate / TBSCertificate / Version
                ASN1 v = decoder[0][tbs];
                version = 1;			// DEFAULT v1
                if ((v.Tag == 0xA0) && (v.Count > 0))
                {
                    // version (optional) is present only in v2+ certs
                    version += v[0].Value[0];	// zero based
                    tbs++;
                }

                // Certificate / TBSCertificate / CertificateSerialNumber
                ASN1 sn = decoder[0][tbs++];
                if (sn.Tag != 0x02)
                    throw new CryptographicException(e);
                serialnumber = sn.Value;
                Array.Reverse(serialnumber, 0, serialnumber.Length);

                // Certificate / TBSCertificate / AlgorithmIdentifier
                tbs++;
                // ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30); 

                ASN1 issuer = tbsCertificate.Element(tbs++, 0x30);
                m_issuername = X501.ToString(issuer);

                ASN1 validity = tbsCertificate.Element(tbs++, 0x30);
                ASN1 notBefore = validity[0];
                m_from = ASN1Convert.ToDateTime(notBefore);
                ASN1 notAfter = validity[1];
                m_until = ASN1Convert.ToDateTime(notAfter);

                ASN1 subject = tbsCertificate.Element(tbs++, 0x30);
                m_subject = X501.ToString(subject);

                ASN1 subjectPublicKeyInfo = tbsCertificate.Element(tbs++, 0x30);

                ASN1 algorithm = subjectPublicKeyInfo.Element(0, 0x30);
                ASN1 algo = algorithm.Element(0, 0x06);
                m_keyalgo = ASN1Convert.ToOid(algo);
                // parameters ANY DEFINED BY algorithm OPTIONAL
                // so we dont ask for a specific (Element) type and return DER
                ASN1 parameters = algorithm[1];
                m_keyalgoparams = ((algorithm.Count > 1) ? parameters.GetBytes() : null);

                ASN1 subjectPublicKey = subjectPublicKeyInfo.Element(1, 0x03);
                // we must drop th first byte (which is the number of unused bits
                // in the BITSTRING)
                int n = subjectPublicKey.Length - 1;
                m_publickey = new byte[n];
                Buffer.BlockCopy(subjectPublicKey.Value, 1, m_publickey, 0, n);

                // signature processing
                byte[] bitstring = decoder[2].Value;
                // first byte contains unused bits in first byte
                signature = new byte[bitstring.Length - 1];
                Buffer.BlockCopy(bitstring, 1, signature, 0, signature.Length);

                algorithm = decoder[1];
                algo = algorithm.Element(0, 0x06);
                m_signaturealgo = ASN1Convert.ToOid(algo);
                parameters = algorithm[1];
                if (parameters != null)
                    m_signaturealgoparams = parameters.GetBytes();
                else
                    m_signaturealgoparams = null;

                // Certificate / TBSCertificate / issuerUniqueID
                ASN1 issuerUID = tbsCertificate.Element(tbs, 0xA1);
                if (issuerUID != null)
                {
                    tbs++;
                    //					issuerUniqueID = issuerUID.Value;
                }

                // Certificate / TBSCertificate / subjectUniqueID
                ASN1 subjectUID = tbsCertificate.Element(tbs, 0xA2);
                if (subjectUID != null)
                {
                    tbs++;
                    //					subjectUniqueID = subjectUID.Value;
                }

                // Certificate / TBSCertificate / Extensions
                ASN1 extns = tbsCertificate.Element(tbs, 0xA3);
                if ((extns != null) && (extns.Count == 1))
                    extensions = new X509ExtensionCollection(extns[0]);
                else
                    extensions = new X509ExtensionCollection(null);

                // keep a copy of the original data
                m_encodedcert = (byte[])data.Clone();
            }
            catch (Exception ex)
            {
                throw new CryptographicException(e, ex);
            }
        }

        // constructors

        public X509Certificate(byte[] data)
        {
            if (data != null)
                Parse(data);
        }

        private byte[] GetUnsignedBigInteger(byte[] integer)
        {
            if (integer[0] == 0x00)
            {
                // this first byte is added so we're sure it's an unsigned integer
                // however we can't feed it into RSAParameters or DSAParameters
                int length = integer.Length - 1;
                byte[] uinteger = new byte[length];
                Buffer.BlockCopy(integer, 1, uinteger, 0, length);
                return uinteger;
            }
            else
                return integer;
        }

        // public methods

        public DSA DSA
        {
            get
            {
                if (_dsa == null)
                {
                    DSAParameters dsaParams = new DSAParameters();
                    // for DSA m_publickey contains 1 ASN.1 integer - Y
                    ASN1 pubkey = new ASN1(m_publickey);
                    if ((pubkey == null) || (pubkey.Tag != 0x02))
                        return null;
                    dsaParams.Y = GetUnsignedBigInteger(pubkey.Value);

                    ASN1 param = new ASN1(m_keyalgoparams);
                    if ((param == null) || (param.Tag != 0x30) || (param.Count < 3))
                        return null;
                    if ((param[0].Tag != 0x02) || (param[1].Tag != 0x02) || (param[2].Tag != 0x02))
                        return null;
                    dsaParams.P = GetUnsignedBigInteger(param[0].Value);
                    dsaParams.Q = GetUnsignedBigInteger(param[1].Value);
                    dsaParams.G = GetUnsignedBigInteger(param[2].Value);

                    // BUG: MS BCL 1.0 can't import a key which 
                    // isn't the same size as the one present in
                    // the container.
                    _dsa = (DSA)new DSACryptoServiceProvider(dsaParams.Y.Length << 3);
                    _dsa.ImportParameters(dsaParams);
                }
                return _dsa;
            }
#if NET_2_0
			set {
				_dsa = value;
				_rsa = null;
			}
#endif
        }

        public X509ExtensionCollection Extensions
        {
            get { return extensions; }
        }

        public byte[] Hash
        {
            get
            {
                if (certhash == null)
                {
                    HashAlgorithm hash = null;
                    switch (m_signaturealgo)
                    {
                        case "1.2.840.113549.1.1.2":	// MD2 with RSA encryption 
                            // maybe someone installed MD2 ?
#if INSIDE_CORLIB
							hash = HashAlgorithm.Create ("MD2");
#else
                            hash = MD2.Create();
#endif
                            break;
                        case "1.2.840.113549.1.1.4":	// MD5 with RSA encryption 
                            hash = MD5.Create();
                            break;
                        case "1.2.840.113549.1.1.5":	// SHA-1 with RSA Encryption 
                        case "1.3.14.3.2.29":		// SHA1 with RSA signature 
                        case "1.2.840.10040.4.3":	// SHA1-1 with DSA
                            hash = SHA1.Create();
                            break;
                        default:
                            return null;
                    }
                    if ((decoder == null) || (decoder.Count < 1))
                        return null;
                    byte[] toBeSigned = decoder[0].GetBytes();
                    certhash = hash.ComputeHash(toBeSigned, 0, toBeSigned.Length);
                }
                return (byte[])certhash.Clone();
            }
        }

        public virtual string IssuerName
        {
            get { return m_issuername; }
        }

        public virtual string KeyAlgorithm
        {
            get { return m_keyalgo; }
        }

        public virtual byte[] KeyAlgorithmParameters
        {
            get
            {
                if (m_keyalgoparams == null)
                    return null;
                return (byte[])m_keyalgoparams.Clone();
            }
        }

        public virtual byte[] PublicKey
        {
            get
            {
                if (m_publickey == null)
                    return null;
                return (byte[])m_publickey.Clone();
            }
        }

        public virtual RSA RSA
        {
            get
            {
                if (_rsa == null)
                {
                    RSAParameters rsaParams = new RSAParameters();
                    // for RSA m_publickey contains 2 ASN.1 integers
                    // the modulus and the public exponent
                    ASN1 pubkey = new ASN1(m_publickey);
                    ASN1 modulus = pubkey[0];
                    if ((modulus == null) || (modulus.Tag != 0x02))
                        return null;
                    ASN1 exponent = pubkey[1];
                    if (exponent.Tag != 0x02)
                        return null;

                    rsaParams.Modulus = GetUnsignedBigInteger(modulus.Value);
                    rsaParams.Exponent = exponent.Value;

                    // BUG: MS BCL 1.0 can't import a key which 
                    // isn't the same size as the one present in
                    // the container.
                    int keySize = (rsaParams.Modulus.Length << 3);
                    _rsa = (RSA)new RSACryptoServiceProvider(keySize);
                    _rsa.ImportParameters(rsaParams);
                }
                return _rsa;
            }
#if NET_2_0
			set {
				_dsa = null;
				_rsa = value;
			}
#endif
        }

        public virtual byte[] RawData
        {
            get
            {
                if (m_encodedcert == null)
                    return null;
                return (byte[])m_encodedcert.Clone();
            }
        }

        public virtual byte[] SerialNumber
        {
            get
            {
                if (serialnumber == null)
                    return null;
                return (byte[])serialnumber.Clone();
            }
        }

        public virtual byte[] Signature
        {
            get
            {
                if (signature == null)
                    return null;

                switch (m_signaturealgo)
                {
                    case "1.2.840.113549.1.1.2":	// MD2 with RSA encryption 
                    case "1.2.840.113549.1.1.4":	// MD5 with RSA encryption 
                    case "1.2.840.113549.1.1.5":	// SHA-1 with RSA Encryption 
                    case "1.3.14.3.2.29":		// SHA1 with RSA signature
                        return (byte[])signature.Clone();

                    case "1.2.840.10040.4.3":	// SHA-1 with DSA
                        ASN1 sign = new ASN1(signature);
                        if ((sign == null) || (sign.Count != 2))
                            return null;
                        // parts may be less than 20 bytes (i.e. first bytes were 0x00)
                        byte[] part1 = sign[0].Value;
                        byte[] part2 = sign[1].Value;
                        byte[] sig = new byte[40];
                        Buffer.BlockCopy(part1, 0, sig, (20 - part1.Length), part1.Length);
                        Buffer.BlockCopy(part2, 0, sig, (40 - part2.Length), part2.Length);
                        return sig;

                    default:
                        throw new CryptographicException("Unsupported hash algorithm: " + m_signaturealgo);
                }
            }
        }

        public virtual string SignatureAlgorithm
        {
            get { return m_signaturealgo; }
        }

        public virtual byte[] SignatureAlgorithmParameters
        {
            get
            {
                if (m_signaturealgoparams == null)
                    return m_signaturealgoparams;
                return (byte[])m_signaturealgoparams.Clone();
            }
        }

        public virtual string SubjectName
        {
            get { return m_subject; }
        }

        public virtual DateTime ValidFrom
        {
            get { return m_from; }
        }

        public virtual DateTime ValidUntil
        {
            get { return m_until; }
        }

        public int Version
        {
            get { return version; }
        }

        public bool IsCurrent
        {
            get { return WasCurrent(DateTime.Now); }
        }

        public bool WasCurrent(DateTime instant)
        {
            return ((instant > ValidFrom) && (instant <= ValidUntil));
        }

        internal bool VerifySignature(DSA dsa)
        {
            // signatureOID is check by both this.Hash and this.Signature
            DSASignatureDeformatter v = new DSASignatureDeformatter(dsa);
            // only SHA-1 is supported
            v.SetHashAlgorithm("SHA1");
            return v.VerifySignature(this.Hash, this.Signature);
        }

        internal bool VerifySignature(RSA rsa)
        {
            RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter(rsa);
            switch (m_signaturealgo)
            {
                // MD2 with RSA encryption 
                case "1.2.840.113549.1.1.2":
                    // maybe someone installed MD2 ?
                    v.SetHashAlgorithm("MD2");
                    break;
                // MD5 with RSA encryption 
                case "1.2.840.113549.1.1.4":
                    v.SetHashAlgorithm("MD5");
                    break;
                // SHA-1 with RSA Encryption 
                case "1.2.840.113549.1.1.5":
                case "1.3.14.3.2.29":
                    v.SetHashAlgorithm("SHA1");
                    break;
                default:
                    throw new CryptographicException("Unsupported hash algorithm: " + m_signaturealgo);
            }
            return v.VerifySignature(this.Hash, this.Signature);
        }

        public bool VerifySignature(AsymmetricAlgorithm aa)
        {
            if (aa == null)
                throw new ArgumentNullException("aa");

            if (aa is RSA)
                return VerifySignature(aa as RSA);
            else if (aa is DSA)
                return VerifySignature(aa as DSA);
            else
                throw new NotSupportedException("Unknown Asymmetric Algorithm " + aa.ToString());
        }

        public bool CheckSignature(byte[] hash, string hashAlgorithm, byte[] signature)
        {
            RSACryptoServiceProvider r = (RSACryptoServiceProvider)RSA;
            return r.VerifyHash(hash, hashAlgorithm, signature);
        }

        public bool IsSelfSigned
        {
            get
            {
                if (m_issuername == m_subject)
                    return VerifySignature(RSA);
                else
                    return false;
            }
        }

#if INSIDE_CORLIB || NET_2_0
		protected X509Certificate (SerializationInfo info, StreamingContext context)
		{
			Parse ((byte[]) info.GetValue ("raw", typeof (byte[])));
		}

		[SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
		public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
		{
			info.AddValue ("raw", m_encodedcert);
			// note: we NEVER serialize the private key
		}
#endif
    }

    // From RFC3280
    /*
     * Certificate  ::=  SEQUENCE  {
     *      tbsCertificate       TBSCertificate,
     *      signatureAlgorithm   AlgorithmIdentifier,
     *      signature            BIT STRING  
     * }
     * TBSCertificate  ::=  SEQUENCE  {
     *      version         [0]  Version DEFAULT v1,
     *      serialNumber         CertificateSerialNumber,
     *      signature            AlgorithmIdentifier,
     *      issuer               Name,
     *      validity             Validity,
     *      subject              Name,
     *      subjectPublicKeyInfo SubjectPublicKeyInfo,
     *      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
     *                           -- If present, version MUST be v2 or v3
     *      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
     *                           -- If present, version MUST be v2 or v3
     *      extensions      [3]  Extensions OPTIONAL
     *                           -- If present, version MUST be v3 --  
     * }
     * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
     * CertificateSerialNumber  ::=  INTEGER
     * Validity ::= SEQUENCE {
     *      notBefore      Time,
     *      notAfter       Time  
     * }
     * Time ::= CHOICE {
     *      utcTime        UTCTime,
     *      generalTime    GeneralizedTime 
     * }
     */
    public class X509CertificateBuilder : X509Builder
    {

        private byte version;
        private byte[] sn;
        private string issuer;
        private DateTime notBefore;
        private DateTime notAfter;
        private string subject;
        private AsymmetricAlgorithm aa;
        private byte[] issuerUniqueID;
        private byte[] subjectUniqueID;
        private X509ExtensionCollection extensions;

        public X509CertificateBuilder() : this(3) { }

        public X509CertificateBuilder(byte version)
        {
            if (version > 3)
                throw new ArgumentException("Invalid certificate version");
            this.version = version;
            extensions = new X509ExtensionCollection();
        }

        public byte Version
        {
            get { return version; }
            set { version = value; }
        }

        public byte[] SerialNumber
        {
            get { return sn; }
            set { sn = value; }
        }

        public string IssuerName
        {
            get { return issuer; }
            set { issuer = value; }
        }

        public DateTime NotBefore
        {
            get { return notBefore; }
            set { notBefore = value; }
        }

        public DateTime NotAfter
        {
            get { return notAfter; }
            set { notAfter = value; }
        }

        public string SubjectName
        {
            get { return subject; }
            set { subject = value; }
        }

        public AsymmetricAlgorithm SubjectPublicKey
        {
            get { return aa; }
            set { aa = value; }
        }

        public byte[] IssuerUniqueId
        {
            get { return issuerUniqueID; }
            set { issuerUniqueID = value; }
        }

        public byte[] SubjectUniqueId
        {
            get { return subjectUniqueID; }
            set { subjectUniqueID = value; }
        }

        public X509ExtensionCollection Extensions
        {
            get { return extensions; }
        }


        /* SubjectPublicKeyInfo  ::=  SEQUENCE  {
         *      algorithm            AlgorithmIdentifier,
         *      subjectPublicKey     BIT STRING  }
         */
        private ASN1 SubjectPublicKeyInfo()
        {
            ASN1 keyInfo = new ASN1(0x30);
            if (aa is RSA)
            {
                keyInfo.Add(PKCS7.AlgorithmIdentifier("1.2.840.113549.1.1.1"));
                RSAParameters p = (aa as RSA).ExportParameters(false);
                /* RSAPublicKey ::= SEQUENCE {
                 *       modulus            INTEGER,    -- n
                 *       publicExponent     INTEGER  }  -- e
                 */
                ASN1 key = new ASN1(0x30);
                key.Add(ASN1Convert.FromUnsignedBigInteger(p.Modulus));
                key.Add(ASN1Convert.FromUnsignedBigInteger(p.Exponent));
                keyInfo.Add(new ASN1(UniqueIdentifier(key.GetBytes())));
            }
            else if (aa is DSA)
            {
                DSAParameters p = (aa as DSA).ExportParameters(false);
                /* Dss-Parms  ::=  SEQUENCE  {
                 *       p             INTEGER,
                 *       q             INTEGER,
                 *       g             INTEGER  }
                 */
                ASN1 param = new ASN1(0x30);
                param.Add(ASN1Convert.FromUnsignedBigInteger(p.P));
                param.Add(ASN1Convert.FromUnsignedBigInteger(p.Q));
                param.Add(ASN1Convert.FromUnsignedBigInteger(p.G));
                keyInfo.Add(PKCS7.AlgorithmIdentifier("1.2.840.10040.4.1", param));
                ASN1 key = keyInfo.Add(new ASN1(0x03));
                // DSAPublicKey ::= INTEGER  -- public key, y
                key.Add(ASN1Convert.FromUnsignedBigInteger(p.Y));
            }
            else
                throw new NotSupportedException("Unknown Asymmetric Algorithm " + aa.ToString());
            return keyInfo;
        }

        private byte[] UniqueIdentifier(byte[] id)
        {
            // UniqueIdentifier  ::=  BIT STRING
            ASN1 uid = new ASN1(0x03);
            // first byte in a BITSTRING is the number of unused bits in the first byte
            byte[] v = new byte[id.Length + 1];
            Buffer.BlockCopy(id, 0, v, 1, id.Length);
            uid.Value = v;
            return uid.GetBytes();
        }

        protected override ASN1 ToBeSigned(string oid)
        {
            // TBSCertificate
            ASN1 tbsCert = new ASN1(0x30);

            if (version > 1)
            {
                // TBSCertificate / [0] Version DEFAULT v1,
                byte[] ver = { (byte)(version - 1) };
                ASN1 v = tbsCert.Add(new ASN1(0xA0));
                v.Add(new ASN1(0x02, ver));
            }

            // TBSCertificate / CertificateSerialNumber,
            tbsCert.Add(new ASN1(0x02, sn));

            // TBSCertificate / AlgorithmIdentifier,
            tbsCert.Add(PKCS7.AlgorithmIdentifier(oid));

            // TBSCertificate / Name
            tbsCert.Add(X501.FromString(issuer));

            // TBSCertificate / Validity
            ASN1 validity = tbsCert.Add(new ASN1(0x30));
            // TBSCertificate / Validity / Time
            validity.Add(ASN1Convert.FromDateTime(notBefore));
            // TBSCertificate / Validity / Time
            validity.Add(ASN1Convert.FromDateTime(notAfter));

            // TBSCertificate / Name
            tbsCert.Add(X501.FromString(subject));

            // TBSCertificate / SubjectPublicKeyInfo
            tbsCert.Add(SubjectPublicKeyInfo());

            if (version > 1)
            {
                // TBSCertificate / [1]  IMPLICIT UniqueIdentifier OPTIONAL
                if (issuerUniqueID != null)
                    tbsCert.Add(new ASN1(0xA1, UniqueIdentifier(issuerUniqueID)));

                // TBSCertificate / [2]  IMPLICIT UniqueIdentifier OPTIONAL
                if (subjectUniqueID != null)
                    tbsCert.Add(new ASN1(0xA1, UniqueIdentifier(subjectUniqueID)));

                // TBSCertificate / [3]  Extensions OPTIONAL
                if ((version > 2) && (extensions.Count > 0))
                    tbsCert.Add(new ASN1(0xA3, extensions.GetBytes()));
            }

            return tbsCert;
        }
    }

    [Serializable]
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class X509CertificateCollection : CollectionBase, IEnumerable
    {

        public X509CertificateCollection()
        {
        }

        public X509CertificateCollection(X509Certificate[] value)
        {
            AddRange(value);
        }

        public X509CertificateCollection(X509CertificateCollection value)
        {
            AddRange(value);
        }

        // Properties

        public X509Certificate this[int index]
        {
            get { return (X509Certificate)InnerList[index]; }
            set { InnerList[index] = value; }
        }

        // Methods

        public int Add(X509Certificate value)
        {
            if (value == null)
                throw new ArgumentNullException("value");

            return InnerList.Add(value);
        }

        public void AddRange(X509Certificate[] value)
        {
            if (value == null)
                throw new ArgumentNullException("value");

            for (int i = 0; i < value.Length; i++)
                InnerList.Add(value[i]);
        }

        public void AddRange(X509CertificateCollection value)
        {
            if (value == null)
                throw new ArgumentNullException("value");

            for (int i = 0; i < value.InnerList.Count; i++)
                InnerList.Add(value[i]);
        }

        public bool Contains(X509Certificate value)
        {
            return (IndexOf(value) != -1);
        }

        public void CopyTo(X509Certificate[] array, int index)
        {
            InnerList.CopyTo(array, index);
        }

        public new X509CertificateEnumerator GetEnumerator()
        {
            return new X509CertificateEnumerator(this);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return InnerList.GetEnumerator();
        }

        public override int GetHashCode()
        {
            return InnerList.GetHashCode();
        }

        public int IndexOf(X509Certificate value)
        {
            if (value == null)
                throw new ArgumentNullException("value");

            byte[] hash = value.Hash;
            for (int i = 0; i < InnerList.Count; i++)
            {
                X509Certificate x509 = (X509Certificate)InnerList[i];
                if (Compare(x509.Hash, hash))
                    return i;
            }
            return -1;
        }

        public void Insert(int index, X509Certificate value)
        {
            InnerList.Insert(index, value);
        }

        public void Remove(X509Certificate value)
        {
            InnerList.Remove(value);
        }

        // private stuff

        private bool Compare(byte[] array1, byte[] array2)
        {
            if ((array1 == null) && (array2 == null))
                return true;
            if ((array1 == null) || (array2 == null))
                return false;
            if (array1.Length != array2.Length)
                return false;
            for (int i = 0; i < array1.Length; i++)
            {
                if (array1[i] != array2[i])
                    return false;
            }
            return true;
        }

        // Inner Class

        public class X509CertificateEnumerator : IEnumerator
        {

            private IEnumerator enumerator;

            // Constructors

            public X509CertificateEnumerator(X509CertificateCollection mappings)
            {
                enumerator = ((IEnumerable)mappings).GetEnumerator();
            }

            // Properties

            public X509Certificate Current
            {
                get { return (X509Certificate)enumerator.Current; }
            }

            object IEnumerator.Current
            {
                get { return enumerator.Current; }
            }

            // Methods

            bool IEnumerator.MoveNext()
            {
                return enumerator.MoveNext();
            }

            void IEnumerator.Reset()
            {
                enumerator.Reset();
            }

            public bool MoveNext()
            {
                return enumerator.MoveNext();
            }

            public void Reset()
            {
                enumerator.Reset();
            }
        }
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class X509Chain
    {

        private X509CertificateCollection roots;
        private X509CertificateCollection certs;
        private X509Certificate _root;

        private X509CertificateCollection _chain;
        private X509ChainStatusFlags _status;

        // constructors

        public X509Chain()
        {
            certs = new X509CertificateCollection();
        }

        // get a pre-builded chain
        public X509Chain(X509CertificateCollection chain)
            : this()
        {
            _chain = new X509CertificateCollection();
            _chain.AddRange(chain);
        }

        // properties

        public X509CertificateCollection Chain
        {
            get { return _chain; }
        }

        // the root of the specified certificate (may not be trusted!)
        public X509Certificate Root
        {
            get { return _root; }
        }

        public X509ChainStatusFlags Status
        {
            get { return _status; }
        }

        public X509CertificateCollection TrustAnchors
        {
            get
            {
                if (roots == null)
                {
                    roots = new X509CertificateCollection();
                    roots.AddRange(X509StoreManager.TrustedRootCertificates);
                    return roots;
                }
                return roots;
            }
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlPolicy)]
            set { roots = value; }
        }

        // methods

        public void LoadCertificate(X509Certificate x509)
        {
            certs.Add(x509);
        }

        public void LoadCertificates(X509CertificateCollection collection)
        {
            certs.AddRange(collection);
        }

        public X509Certificate FindByIssuerName(string issuerName)
        {
            foreach (X509Certificate x in certs)
            {
                if (x.IssuerName == issuerName)
                    return x;
            }
            return null;
        }

        public bool Build(X509Certificate leaf)
        {
            _status = X509ChainStatusFlags.NoError;
            if (_chain == null)
            {
                // chain not supplied - we must build it ourselve
                _chain = new X509CertificateCollection();
                X509Certificate x = leaf;
                X509Certificate tmp = x;
                while ((x != null) && (!x.IsSelfSigned))
                {
                    tmp = x; // last valid
                    _chain.Add(x);
                    x = FindCertificateParent(x);
                }
                // find a trusted root
                _root = FindCertificateRoot(tmp);
            }
            else
            {
                // chain supplied - still have to check signatures!
                int last = _chain.Count;
                if (last > 0)
                {
                    if (IsParent(leaf, _chain[0]))
                    {
                        int i = 1;
                        for (; i < last; i++)
                        {
                            if (!IsParent(_chain[i - 1], _chain[i]))
                                break;
                        }
                        if (i == last)
                            _root = FindCertificateRoot(_chain[last - 1]);
                    }
                }
                else
                {
                    // is the leaf a root ? (trusted or untrusted)
                    _root = FindCertificateRoot(leaf);
                }
            }

            // validate the chain
            if ((_chain != null) && (_status == X509ChainStatusFlags.NoError))
            {
                foreach (X509Certificate x in _chain)
                {
                    // validate dates for each certificate in the chain
                    // note: we DO NOT check for nested date/time
                    if (!IsValid(x))
                    {
                        return false;
                    }
                }
                // check leaf
                if (!IsValid(leaf))
                {
                    // switch status code if the failure is expiration
                    if (_status == X509ChainStatusFlags.NotTimeNested)
                        _status = X509ChainStatusFlags.NotTimeValid;
                    return false;
                }
                // check root
                if ((_root != null) && !IsValid(_root))
                {
                    return false;
                }
            }
            return (_status == X509ChainStatusFlags.NoError);
        }

        //

        public void Reset()
        {
            _status = X509ChainStatusFlags.NoError;
            roots = null; // this force a reload
            certs.Clear();
            if (_chain != null)
                _chain.Clear();
        }

        // private stuff

        private bool IsValid(X509Certificate cert)
        {
            if (!cert.IsCurrent)
            {
                // FIXME: nesting isn't very well implemented
                _status = X509ChainStatusFlags.NotTimeNested;
                return false;
            }

            // TODO - we should check for CRITICAL but unknown extensions
            // X509ChainStatusFlags.InvalidExtension
#if (!NET_1_0 && !INSIDE_CORLIB)
            if (ServicePointManager.CheckCertificateRevocationList)
            {
                // TODO - check revocation (CRL, OCSP ...)
                // X509ChainStatusFlags.RevocationStatusUnknown
                // X509ChainStatusFlags.Revoked
            }
#endif
            return true;
        }

        private X509Certificate FindCertificateParent(X509Certificate child)
        {
            foreach (X509Certificate potentialParent in certs)
            {
                if (IsParent(child, potentialParent))
                    return potentialParent;
            }
            return null;
        }

        private X509Certificate FindCertificateRoot(X509Certificate potentialRoot)
        {
            if (potentialRoot == null)
            {
                _status = X509ChainStatusFlags.PartialChain;
                return null;
            }

            // if the trusted root is in the chain
            if (IsTrusted(potentialRoot))
            {
                return potentialRoot;
            }

            // if the root isn't in the chain
            foreach (X509Certificate root in TrustAnchors)
            {
                if (IsParent(potentialRoot, root))
                {
                    return root;
                }
            }

            // is it a (untrusted) root ?
            if (potentialRoot.IsSelfSigned)
            {
                _status = X509ChainStatusFlags.UntrustedRoot;
                return potentialRoot;
            }

            _status = X509ChainStatusFlags.PartialChain;
            return null;
        }

        private bool IsTrusted(X509Certificate potentialTrusted)
        {
            return TrustAnchors.Contains(potentialTrusted);
        }

        private bool IsParent(X509Certificate child, X509Certificate parent)
        {
            if (child.IssuerName != parent.SubjectName)
                return false;

            // parent MUST have the Basic Constraint CA=true (except for trusted roots)
            // see why at http://www.microsoft.com/technet/security/bulletin/MS02-050.asp
            if ((parent.Version > 2) && (!IsTrusted(parent)))
            {
                // TODO: we do not support pathLenConstraint
                X509Extension ext = parent.Extensions["2.5.29.19"];
                if (ext != null)
                {
                    BasicConstraintsExtension bc = new BasicConstraintsExtension(ext);
                    if (!bc.CertificateAuthority)
                        _status = X509ChainStatusFlags.InvalidBasicConstraints;
                }
                else
                    _status = X509ChainStatusFlags.InvalidBasicConstraints;
            }

            if (!child.VerifySignature(parent.RSA))
            {
                _status = X509ChainStatusFlags.NotSignatureValid;
                return false;
            }
            return true;
        }
    }

    [Serializable]
    [Flags]
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 enum X509ChainStatusFlags
    {
        //		CtlNotSignatureValid = 262144,
        //		CtlNotTimeValid = 131072,
        //		CtlNotValidForUsage = 524288,
        //		Cyclic = 128,
        //		HasExcludedNameConstraint = 32768,
        //		HasNotDefinedNameConstraint = 8192,
        //		HasNotPermittedNameConstraint = 16384,
        //		HasNotSupportedNameConstraint = 4096,
        InvalidBasicConstraints = 1024,
        //		InvalidExtension = 256,
        //		InvalidNameConstraints = 2048,
        //		InvalidPolicyConstraints = 512,
        NoError = 0,
        //		NoIssuanceChainPolicy = 33554432,
        NotSignatureValid = 8,
        NotTimeNested = 2,
        NotTimeValid = 1,
        //		NotValidForUsage = 16,
        //		OfflineRevocation = 16777216,
        PartialChain = 65536,
        //		RevocationStatusUnknown = 64,
        //		Revoked = 4,
        UntrustedRoot = 32
    }

    /*
 * CertificateList  ::=  SEQUENCE  {
 *	tbsCertList          TBSCertList,
 *	signatureAlgorithm   AlgorithmIdentifier,
 *	signature            BIT STRING  
 * }
 * 
 * TBSCertList  ::=  SEQUENCE  {
 *	version                 Version OPTIONAL,
 *		-- if present, MUST be v2
 *	signature               AlgorithmIdentifier,
 *	issuer                  Name,
 *	thisUpdate              Time,
 *	nextUpdate              Time OPTIONAL,
 *	revokedCertificates     SEQUENCE OF SEQUENCE  {
 *		userCertificate         CertificateSerialNumber,
 *		revocationDate          Time,
 *		crlEntryExtensions      Extensions OPTIONAL
 *			-- if present, MUST be v2
 *	}  OPTIONAL,
 *	crlExtensions           [0] Extensions OPTIONAL }
 *		-- if present, MUST be v2
 */
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class X509Crl
    {

        public class X509CrlEntry
        {

            private byte[] sn;
            private DateTime revocationDate;
            private X509ExtensionCollection extensions;

            internal X509CrlEntry(byte[] serialNumber, DateTime revocationDate, X509ExtensionCollection extensions)
            {
                sn = serialNumber;
                this.revocationDate = revocationDate;
                if (extensions == null)
                    this.extensions = new X509ExtensionCollection();
                else
                    this.extensions = extensions;
            }

            internal X509CrlEntry(ASN1 entry)
            {
                sn = entry[0].Value;
                Array.Reverse(sn);
                revocationDate = ASN1Convert.ToDateTime(entry[1]);
                extensions = new X509ExtensionCollection(entry[2]);
            }

            public byte[] SerialNumber
            {
                get { return (byte[])sn.Clone(); }
            }

            public DateTime RevocationDate
            {
                get { return revocationDate; }
            }

            public X509ExtensionCollection Extensions
            {
                get { return extensions; }
            }

            public byte[] GetBytes()
            {
                ASN1 sequence = new ASN1(0x30);
                sequence.Add(new ASN1(0x02, sn));
                sequence.Add(ASN1Convert.FromDateTime(revocationDate));
                if (extensions.Count > 0)
                    sequence.Add(new ASN1(extensions.GetBytes()));
                return sequence.GetBytes();
            }
        }

        private string issuer;
        private byte version;
        private DateTime thisUpdate;
        private DateTime nextUpdate;
        private ArrayList entries;
        private string signatureOID;
        private byte[] signature;
        private X509ExtensionCollection extensions;
        private byte[] encoded;

        public X509Crl(byte[] crl)
        {
            if (crl == null)
                throw new ArgumentNullException("crl");
            encoded = (byte[])crl.Clone();
            Parse(encoded);
        }

        private void Parse(byte[] crl)
        {
            string e = "Input data cannot be coded as a valid CRL.";
            try
            {
                // CertificateList  ::=  SEQUENCE  {
                ASN1 encodedCRL = new ASN1(encoded);
                if ((encodedCRL.Tag != 0x30) || (encodedCRL.Count != 3))
                    throw new CryptographicException(e);

                // CertificateList / TBSCertList,
                ASN1 toBeSigned = encodedCRL[0];
                if ((toBeSigned.Tag != 0x30) || (toBeSigned.Count < 3))
                    throw new CryptographicException(e);

                int n = 0;
                // CertificateList / TBSCertList / Version OPTIONAL, -- if present, MUST be v2
                if (toBeSigned[n].Tag == 0x02)
                {
                    version = (byte)(toBeSigned[n++].Value[0] + 1);
                }
                else
                    version = 1; // DEFAULT
                // CertificateList / TBSCertList / AlgorithmIdentifier,
                signatureOID = ASN1Convert.ToOid(toBeSigned[n++][0]);
                // CertificateList / TBSCertList / Name,
                issuer = X501.ToString(toBeSigned[n++]);
                // CertificateList / TBSCertList / Time,
                thisUpdate = ASN1Convert.ToDateTime(toBeSigned[n++]);
                // CertificateList / TBSCertList / Time OPTIONAL,
                ASN1 next = toBeSigned[n++];
                if ((next.Tag == 0x17) || (next.Tag == 0x18))
                {
                    nextUpdate = ASN1Convert.ToDateTime(next);
                    next = toBeSigned[n++];
                }
                // CertificateList / TBSCertList / revokedCertificates	SEQUENCE OF SEQUENCE  {
                entries = new ArrayList();
                // this is OPTIONAL so it may not be present if no entries exists
                if ((next != null) && (next.Tag == 0x30))
                {
                    ASN1 revokedCertificates = next;
                    for (int i = 0; i < revokedCertificates.Count; i++)
                    {
                        entries.Add(new X509CrlEntry(revokedCertificates[i]));
                    }
                }
                else
                {
                    n--;
                }
                // CertificateList / TBSCertList / crlExtensions [0] Extensions OPTIONAL }
                ASN1 extns = toBeSigned[n];
                if ((extns != null) && (extns.Tag == 0xA0) && (extns.Count == 1))
                    extensions = new X509ExtensionCollection(extns[0]);
                else
                    extensions = new X509ExtensionCollection(null); // result in a read only object
                // CertificateList / AlgorithmIdentifier
                string signatureAlgorithm = ASN1Convert.ToOid(encodedCRL[1][0]);
                if (signatureOID != signatureAlgorithm)
                    throw new CryptographicException(e + " [Non-matching signature algorithms in CRL]");

                // CertificateList / BIT STRING 
                byte[] bitstring = encodedCRL[2].Value;
                // first byte contains unused bits in first byte
                signature = new byte[bitstring.Length - 1];
                Buffer.BlockCopy(bitstring, 1, signature, 0, signature.Length);
            }
            catch
            {
                throw new CryptographicException(e);
            }
        }

        public ArrayList Entries
        {
            get { return ArrayList.ReadOnly(entries); }
        }

        public X509CrlEntry this[int index]
        {
            get { return (X509CrlEntry)entries[index]; }
        }

        public X509CrlEntry this[byte[] serialNumber]
        {
            get { return GetCrlEntry(serialNumber); }
        }

        public X509ExtensionCollection Extensions
        {
            get { return extensions; }
        }

        public string IssuerName
        {
            get { return issuer; }
        }

        public DateTime NextUpdate
        {
            get { return nextUpdate; }
        }

        public DateTime ThisUpdate
        {
            get { return thisUpdate; }
        }

        public string SignatureAlgorithm
        {
            get { return signatureOID; }
        }

        public byte[] Signature
        {
            get
            {
                if (signature == null)
                    return null;
                return (byte[])signature.Clone();
            }
        }

        public byte Version
        {
            get { return version; }
        }

        public bool IsCurrent
        {
            get { return WasCurrent(DateTime.Now); }
        }

        public bool WasCurrent(DateTime instant)
        {
            if (nextUpdate == DateTime.MinValue)
                return (instant >= thisUpdate);
            else
                return ((instant >= thisUpdate) && (instant <= nextUpdate));
        }

        public byte[] GetBytes()
        {
            return (byte[])encoded.Clone();
        }

        private bool Compare(byte[] array1, byte[] array2)
        {
            if ((array1 == null) && (array2 == null))
                return true;
            if ((array1 == null) || (array2 == null))
                return false;
            if (array1.Length != array2.Length)
                return false;
            for (int i = 0; i < array1.Length; i++)
            {
                if (array1[i] != array2[i])
                    return false;
            }
            return true;
        }

        public X509CrlEntry GetCrlEntry(X509Certificate x509)
        {
            if (x509 == null)
                throw new ArgumentNullException("x509");

            return GetCrlEntry(x509.SerialNumber);
        }

        public X509CrlEntry GetCrlEntry(byte[] serialNumber)
        {
            if (serialNumber == null)
                throw new ArgumentNullException("serialNumber");

            for (int i = 0; i < entries.Count; i++)
            {
                X509CrlEntry entry = (X509CrlEntry)entries[i];
                if (Compare(serialNumber, entry.SerialNumber))
                    return entry;
            }
            return null;
        }

        public bool VerifySignature(X509Certificate x509)
        {
            if (x509 == null)
                throw new ArgumentNullException("x509");

            // 1. x509 certificate must be a CA certificate (unknown for v1 or v2 certs)
            if (x509.Version >= 3)
            {
                // 1.1. Check for "cRLSign" bit in KeyUsage extension
                X509Extension ext = x509.Extensions["2.5.29.15"];
                if (ext != null)
                {
                    KeyUsageExtension keyUsage = new KeyUsageExtension(ext);
                    if (!keyUsage.Support(KeyUsages.cRLSign))
                        return false;
                }
                // 1.2. Check for ca = true in BasicConstraint
                ext = x509.Extensions["2.5.29.19"];
                if (ext != null)
                {
                    BasicConstraintsExtension basicConstraints = new BasicConstraintsExtension(ext);
                    if (!basicConstraints.CertificateAuthority)
                        return false;
                }
            }
            // 2. CRL issuer must match CA subject name
            if (issuer != x509.SubjectName)
                return false;
            // 3. Check the CRL signature with the CA certificate public key
            switch (signatureOID)
            {
                case "1.2.840.10040.4.3":
                    return VerifySignature(x509.DSA);
                default:
                    return VerifySignature(x509.RSA);
            }
        }

        private byte[] GetHash(string hashName)
        {
            ASN1 encodedCRL = new ASN1(encoded);
            byte[] toBeSigned = encodedCRL[0].GetBytes();
            HashAlgorithm ha = HashAlgorithm.Create(hashName);
            return ha.ComputeHash(toBeSigned);
        }

        internal bool VerifySignature(DSA dsa)
        {
            if (signatureOID != "1.2.840.10040.4.3")
                throw new CryptographicException("Unsupported hash algorithm: " + signatureOID);
            DSASignatureDeformatter v = new DSASignatureDeformatter(dsa);
            // only SHA-1 is supported
            string hashName = "SHA1";
            v.SetHashAlgorithm(hashName);
            ASN1 sign = new ASN1(signature);
            if ((sign == null) || (sign.Count != 2))
                return false;
            // parts may be less than 20 bytes (i.e. first bytes were 0x00)
            byte[] part1 = sign[0].Value;
            byte[] part2 = sign[1].Value;
            byte[] sig = new byte[40];
            Buffer.BlockCopy(part1, 0, sig, (20 - part1.Length), part1.Length);
            Buffer.BlockCopy(part2, 0, sig, (40 - part2.Length), part2.Length);
            return v.VerifySignature(GetHash(hashName), sig);
        }

        internal bool VerifySignature(RSA rsa)
        {
            RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter(rsa);
            string hashName = null;
            switch (signatureOID)
            {
                // MD2 with RSA encryption 
                case "1.2.840.113549.1.1.2":
                    // maybe someone installed MD2 ?
                    hashName = "MD2";
                    break;
                // MD5 with RSA encryption 
                case "1.2.840.113549.1.1.4":
                    hashName = "MD5";
                    break;
                // SHA-1 with RSA Encryption 
                case "1.2.840.113549.1.1.5":
                    hashName = "SHA1";
                    break;
                default:
                    throw new CryptographicException("Unsupported hash algorithm: " + signatureOID);
            }
            v.SetHashAlgorithm(hashName);
            return v.VerifySignature(GetHash(hashName), signature);
        }

        public bool VerifySignature(AsymmetricAlgorithm aa)
        {
            if (aa == null)
                throw new ArgumentNullException("aa");

            // only validate the signature (in case we don't have the CA certificate)
            if (aa is RSA)
                return VerifySignature(aa as RSA);
            else if (aa is DSA)
                return VerifySignature(aa as DSA);
            else
                throw new NotSupportedException("Unknown Asymmetric Algorithm " + aa.ToString());
        }

        static public X509Crl CreateFromFile(string filename)
        {
            byte[] crl = null;
            using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                crl = new byte[fs.Length];
                fs.Read(crl, 0, crl.Length);
                fs.Close();
            }
            return new X509Crl(crl);
        }
    }

    /*
 * Extension  ::=  SEQUENCE  {
 *	extnID      OBJECT IDENTIFIER,
 *	critical    BOOLEAN DEFAULT FALSE,
 *	extnValue   OCTET STRING  
 * }
 */
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class X509Extension
    {

        protected string extnOid;
        protected bool extnCritical;
        protected ASN1 extnValue;

        protected X509Extension()
        {
            extnCritical = false;
        }

        public X509Extension(ASN1 asn1)
        {
            if ((asn1.Tag != 0x30) || (asn1.Count < 2))
                throw new ArgumentException(("Invalid X.509 extension."));
            if (asn1[0].Tag != 0x06)
                throw new ArgumentException(("Invalid X.509 extension."));

            extnOid = ASN1Convert.ToOid(asn1[0]);
            extnCritical = ((asn1[1].Tag == 0x01) && (asn1[1].Value[0] == 0xFF));
            // last element is an octet string which may need to be decoded
            extnValue = asn1[asn1.Count - 1];
            if ((extnValue.Tag == 0x04) && (extnValue.Length > 0) && (extnValue.Count == 0))
            {
                try
                {
                    ASN1 encapsulated = new ASN1(extnValue.Value);
                    extnValue.Value = null;
                    extnValue.Add(encapsulated);
                }
                catch
                {
                    // data isn't ASN.1
                }
            }
            Decode();
        }

        public X509Extension(X509Extension extension)
        {
            if (extension == null)
                throw new ArgumentNullException("extension");
            if ((extension.Value == null) || (extension.Value.Tag != 0x04) || (extension.Value.Count != 1))
                throw new ArgumentException(("Invalid X.509 extension."));

            extnOid = extension.Oid;
            extnCritical = extension.Critical;
            extnValue = extension.Value;
            Decode();
        }

        // encode the extension *into* an OCTET STRING
        protected virtual void Decode()
        {
        }

        // decode the extension from *inside* an OCTET STRING
        protected virtual void Encode()
        {
        }

        public ASN1 ASN1
        {
            get
            {
                ASN1 extension = new ASN1(0x30);
                extension.Add(ASN1Convert.FromOid(extnOid));
                if (extnCritical)
                    extension.Add(new ASN1(0x01, new byte[1] { 0xFF }));
                Encode();
                extension.Add(extnValue);
                return extension;
            }
        }

        public string Oid
        {
            get { return extnOid; }
        }

        public bool Critical
        {
            get { return extnCritical; }
            set { extnCritical = value; }
        }

        // this gets overrided with more meaningful names
        public virtual string Name
        {
            get { return extnOid; }
        }

        public ASN1 Value
        {
            get
            {
                if (extnValue == null)
                {
                    Encode();
                }
                return extnValue;
            }
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            X509Extension ex = (obj as X509Extension);
            if (ex == null)
                return false;

            if (extnCritical != ex.extnCritical)
                return false;
            if (extnOid != ex.extnOid)
                return false;
            if (extnValue.Length != ex.extnValue.Length)
                return false;

            for (int i = 0; i < extnValue.Length; i++)
            {
                if (extnValue[i] != ex.extnValue[i])
                    return false;
            }
            return true;
        }

        public byte[] GetBytes()
        {
            return ASN1.GetBytes();
        }

        public override int GetHashCode()
        {
            // OID should be unique in a collection of extensions
            return extnOid.GetHashCode();
        }

        private void WriteLine(StringBuilder sb, int n, int pos)
        {
            byte[] value = extnValue.Value;
            int p = pos;
            for (int j = 0; j < 8; j++)
            {
                if (j < n)
                {
                    sb.Append(value[p++].ToString("X2", CultureInfo.InvariantCulture));
                    sb.Append(" ");
                }
                else
                    sb.Append("   ");
            }
            sb.Append("  ");
            p = pos;
            for (int j = 0; j < n; j++)
            {
                byte b = value[p++];
                if (b < 0x20)
                    sb.Append(".");
                else
                    sb.Append(Convert.ToChar(b));
            }
            sb.Append(Environment.NewLine);
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            int div = (extnValue.Length >> 3);
            int rem = (extnValue.Length - (div << 3));
            int x = 0;
            for (int i = 0; i < div; i++)
            {
                WriteLine(sb, 8, x);
                x += 8;
            }
            WriteLine(sb, rem, x);
            return sb.ToString();
        }
    }

    /*
 * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
 * 
 * Note: 1..MAX -> There shouldn't be 0 Extensions in the ASN1 structure
 */
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 sealed class X509ExtensionCollection : CollectionBase, IEnumerable
    {

        private bool readOnly;

        public X509ExtensionCollection()
            : base()
        {
        }

        public X509ExtensionCollection(ASN1 asn1)
            : this()
        {
            readOnly = true;
            if (asn1 == null)
                return;
            if (asn1.Tag != 0x30)
                throw new Exception("Invalid extensions format");
            for (int i = 0; i < asn1.Count; i++)
            {
                X509Extension extension = new X509Extension(asn1[i]);
                InnerList.Add(extension);
            }
        }

        public int Add(X509Extension extension)
        {
            if (extension == null)
                throw new ArgumentNullException("extension");
            if (readOnly)
                throw new NotSupportedException("Extensions are read only");

            return InnerList.Add(extension);
        }

        public void AddRange(X509Extension[] extension)
        {
            if (extension == null)
                throw new ArgumentNullException("extension");
            if (readOnly)
                throw new NotSupportedException("Extensions are read only");

            for (int i = 0; i < extension.Length; i++)
                InnerList.Add(extension[i]);
        }

        public void AddRange(X509ExtensionCollection collection)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");
            if (readOnly)
                throw new NotSupportedException("Extensions are read only");

            for (int i = 0; i < collection.InnerList.Count; i++)
                InnerList.Add(collection[i]);
        }

        public bool Contains(X509Extension extension)
        {
            return (IndexOf(extension) != -1);
        }

        public bool Contains(string oid)
        {
            return (IndexOf(oid) != -1);
        }

        public void CopyTo(X509Extension[] extensions, int index)
        {
            if (extensions == null)
                throw new ArgumentNullException("extensions");

            InnerList.CopyTo(extensions, index);
        }

        public int IndexOf(X509Extension extension)
        {
            if (extension == null)
                throw new ArgumentNullException("extension");

            for (int i = 0; i < InnerList.Count; i++)
            {
                X509Extension ex = (X509Extension)InnerList[i];
                if (ex.Equals(extension))
                    return i;
            }
            return -1;
        }

        public int IndexOf(string oid)
        {
            if (oid == null)
                throw new ArgumentNullException("oid");

            for (int i = 0; i < InnerList.Count; i++)
            {
                X509Extension ex = (X509Extension)InnerList[i];
                if (ex.Oid == oid)
                    return i;
            }
            return -1;
        }

        public void Insert(int index, X509Extension extension)
        {
            if (extension == null)
                throw new ArgumentNullException("extension");

            InnerList.Insert(index, extension);
        }

        public void Remove(X509Extension extension)
        {
            if (extension == null)
                throw new ArgumentNullException("extension");

            InnerList.Remove(extension);
        }

        public void Remove(string oid)
        {
            if (oid == null)
                throw new ArgumentNullException("oid");

            int index = IndexOf(oid);
            if (index != -1)
                InnerList.RemoveAt(index);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return InnerList.GetEnumerator();
        }

        public X509Extension this[int index]
        {
            get { return (X509Extension)InnerList[index]; }
        }

        public X509Extension this[string oid]
        {
            get
            {
                int index = IndexOf(oid);
                if (index == -1)
                    return null;
                return (X509Extension)InnerList[index];
            }
        }

        public byte[] GetBytes()
        {
            if (InnerList.Count < 1)
                return null;
            ASN1 sequence = new ASN1(0x30);
            for (int i = 0; i < InnerList.Count; i++)
            {
                X509Extension x = (X509Extension)InnerList[i];
                sequence.Add(x.ASN1);
            }
            return sequence.GetBytes();
        }
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class X509Store
    {

        private string _storePath;
        private X509CertificateCollection _certificates;
        private ArrayList _crls;
        private bool _crl;
        private string _name;

        internal X509Store(string path, bool crl)
        {
            _storePath = path;
            _crl = crl;
        }

        // properties

        public X509CertificateCollection Certificates
        {
            get
            {
                if (_certificates == null)
                {
                    _certificates = BuildCertificatesCollection(_storePath);
                }
                return _certificates;
            }
        }

        public ArrayList Crls
        {
            get
            {
                // CRL aren't applicable to all stores
                // but returning null is a little rude
                if (!_crl)
                {
                    _crls = new ArrayList();
                }
                if (_crls == null)
                {
                    _crls = BuildCrlsCollection(_storePath);
                }
                return _crls;
            }
        }

        public string Name
        {
            get
            {
                if (_name == null)
                {
                    int n = _storePath.LastIndexOf(Path.DirectorySeparatorChar);
                    _name = _storePath.Substring(n + 1);
                }
                return _name;
            }
        }

        // methods

        public void Clear()
        {
            if (_certificates != null)
                _certificates.Clear();
            _certificates = null;
            if (_crls != null)
                _crls.Clear();
            _crls = null;
        }

        public void Import(X509Certificate certificate)
        {
            CheckStore(_storePath, true);

            string filename = Path.Combine(_storePath, GetUniqueName(certificate));
            if (!File.Exists(filename))
            {
                using (FileStream fs = File.OpenWrite(filename))
                {
                    byte[] data = certificate.RawData;
                    fs.Write(data, 0, data.Length);
                    fs.Close();
                }
            }
        }

        public void Remove(X509Certificate certificate)
        {
            string filename = Path.Combine(_storePath, GetUniqueName(certificate));
            if (File.Exists(filename))
            {
                File.Delete(filename);
            }
        }

        // private stuff

        private string GetUniqueName(X509Certificate certificate)
        {
            string method = null;
            byte[] name = null;

            // We prefer Subject Key Identifier as the unique name
            // as it will provide faster lookups
            X509Extension ext = certificate.Extensions["2.5.29.14"];
            if (ext != null)
            {
                SubjectKeyIdentifierExtension ski = new SubjectKeyIdentifierExtension(ext);
                name = ski.Identifier;
                method = "ski";
            }
            else
            {
                method = "tbp"; // thumbprint
                name = certificate.Hash;
            }

            StringBuilder sb = new StringBuilder(method);
            sb.Append("-");
            foreach (byte b in name)
            {
                sb.Append(b.ToString("X2", CultureInfo.InvariantCulture));
            }
            sb.Append(".cer");

            return sb.ToString();
        }

        private byte[] Load(string filename)
        {
            byte[] data = null;
            using (FileStream fs = File.OpenRead(filename))
            {
                data = new byte[fs.Length];
                fs.Read(data, 0, data.Length);
                fs.Close();
            }
            return data;
        }

        private X509Certificate LoadCertificate(string filename)
        {
            byte[] data = Load(filename);
            X509Certificate cert = new X509Certificate(data);
            return cert;
        }

        private X509Crl LoadCrl(string filename)
        {
            byte[] data = Load(filename);
            X509Crl crl = new X509Crl(data);
            return crl;
        }

        private bool CheckStore(string path, bool throwException)
        {
            try
            {
                if (Directory.Exists(path))
                    return true;
                Directory.CreateDirectory(path);
                return Directory.Exists(path);
            }
            catch
            {
                if (throwException)
                    throw;
                return false;
            }
        }

        private X509CertificateCollection BuildCertificatesCollection(string storeName)
        {
            X509CertificateCollection coll = new X509CertificateCollection();
            string path = Path.Combine(_storePath, storeName);
            if (!CheckStore(path, false))
                return coll;	// empty collection

            string[] files = Directory.GetFiles(path, "*.cer");
            if ((files != null) && (files.Length > 0))
            {
                foreach (string file in files)
                {
                    try
                    {
                        X509Certificate cert = LoadCertificate(file);
                        coll.Add(cert);
                    }
                    catch
                    {
                        // in case someone is dumb enough
                        // (like me) to include a base64
                        // encoded certs (or other junk 
                        // into the store).
                    }
                }
            }
            return coll;
        }

        private ArrayList BuildCrlsCollection(string storeName)
        {
            ArrayList list = new ArrayList();
            string path = Path.Combine(_storePath, storeName);
            if (!CheckStore(path, false))
                return list;	// empty list

            string[] files = Directory.GetFiles(path, "*.crl");
            if ((files != null) && (files.Length > 0))
            {
                foreach (string file in files)
                {
                    try
                    {
                        X509Crl crl = LoadCrl(file);
                        list.Add(crl);
                    }
                    catch
                    {
                        // junk catcher
                    }
                }
            }
            return list;
        }
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 sealed class X509StoreManager
    {

        static private X509Stores _userStore;
        static private X509Stores _machineStore;

        private X509StoreManager()
        {
        }

        static public X509Stores CurrentUser
        {
            get
            {
                if (_userStore == null)
                {
                    string _userPath = Path.Combine(
                        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                        ".mono");
                    _userPath = Path.Combine(_userPath, "certs");

                    _userStore = new X509Stores(_userPath);
                }
                return _userStore;
            }
        }

        static public X509Stores LocalMachine
        {
            get
            {
                if (_machineStore == null)
                {
                    string _machinePath = Path.Combine(
                        Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                        ".mono");
                    _machinePath = Path.Combine(_machinePath, "certs");

                    _machineStore = new X509Stores(_machinePath);
                }
                return _machineStore;
            }
        }

        // Merged stores collections
        // we need to look at both the user and the machine (entreprise)
        // certificates/CRLs when building/validating a chain

        static public X509CertificateCollection IntermediateCACertificates
        {
            get
            {
                X509CertificateCollection intermediateCerts = new X509CertificateCollection();
                intermediateCerts.AddRange(CurrentUser.IntermediateCA.Certificates);
                intermediateCerts.AddRange(LocalMachine.IntermediateCA.Certificates);
                return intermediateCerts;
            }
        }

        static public ArrayList IntermediateCACrls
        {
            get
            {
                ArrayList intermediateCRLs = new ArrayList();
                intermediateCRLs.AddRange(CurrentUser.IntermediateCA.Crls);
                intermediateCRLs.AddRange(LocalMachine.IntermediateCA.Crls);
                return intermediateCRLs;
            }
        }

        static public X509CertificateCollection TrustedRootCertificates
        {
            get
            {
                X509CertificateCollection trustedCerts = new X509CertificateCollection();
                trustedCerts.AddRange(CurrentUser.TrustedRoot.Certificates);
                trustedCerts.AddRange(LocalMachine.TrustedRoot.Certificates);
                return trustedCerts;
            }
        }

        static public ArrayList TrustedRootCACrls
        {
            get
            {
                ArrayList trustedCRLs = new ArrayList();
                trustedCRLs.AddRange(CurrentUser.TrustedRoot.Crls);
                trustedCRLs.AddRange(LocalMachine.TrustedRoot.Crls);
                return trustedCRLs;
            }
        }

        static public X509CertificateCollection UntrustedCertificates
        {
            get
            {
                X509CertificateCollection untrustedCerts = new X509CertificateCollection();
                untrustedCerts.AddRange(CurrentUser.Untrusted.Certificates);
                untrustedCerts.AddRange(LocalMachine.Untrusted.Certificates);
                return untrustedCerts;
            }
        }
    }

#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class X509Stores
    {

        private string _storePath;
        private X509Store _personal;
        private X509Store _other;
        private X509Store _intermediate;
        private X509Store _trusted;
        private X509Store _untrusted;

        internal X509Stores(string path)
        {
            _storePath = path;
        }

        // properties

        public X509Store Personal
        {
            get
            {
                if (_personal == null)
                {
                    string path = Path.Combine(_storePath, Names.Personal);
                    _personal = new X509Store(path, false);
                }
                return _personal;
            }
        }

        public X509Store OtherPeople
        {
            get
            {
                if (_other == null)
                {
                    string path = Path.Combine(_storePath, Names.OtherPeople);
                    _other = new X509Store(path, false);
                }
                return _other;
            }
        }

        public X509Store IntermediateCA
        {
            get
            {
                if (_intermediate == null)
                {
                    string path = Path.Combine(_storePath, Names.IntermediateCA);
                    _intermediate = new X509Store(path, true);
                }
                return _intermediate;
            }
        }

        public X509Store TrustedRoot
        {
            get
            {
                if (_trusted == null)
                {
                    string path = Path.Combine(_storePath, Names.TrustedRoot);
                    _trusted = new X509Store(path, true);
                }
                return _trusted;
            }
        }

        public X509Store Untrusted
        {
            get
            {
                if (_untrusted == null)
                {
                    string path = Path.Combine(_storePath, Names.Untrusted);
                    _untrusted = new X509Store(path, false);
                }
                return _untrusted;
            }
        }

        // methods

        public void Clear()
        {
            // this will force a reload of all stores
            if (_personal != null)
                _personal.Clear();
            _personal = null;
            if (_other != null)
                _other.Clear();
            _other = null;
            if (_intermediate != null)
                _intermediate.Clear();
            _intermediate = null;
            if (_trusted != null)
                _trusted.Clear();
            _trusted = null;
            if (_untrusted != null)
                _untrusted.Clear();
            _untrusted = null;
        }

        // names

        public class Names
        {

            // do not translate
            public const string Personal = "My";
            public const string OtherPeople = "AddressBook";
            public const string IntermediateCA = "CA";
            public const string TrustedRoot = "Trust";
            public const string Untrusted = "Disallowed";

            public Names() { }
        }
    }

    // References:
    // 1.	Information technology - Open Systems Interconnection - The Directory: Selected attribute types 
    //	http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-X.520 
    // 2.	Internet X.509 Public Key Infrastructure Certificate and CRL Profile
    //	http://www.ietf.org/rfc/rfc3280.txt
    // 3.	A Summary of the X.500(96) User Schema for use with LDAPv3
    //	http://www.faqs.org/rfcs/rfc2256.html
    // 4.	RFC 2247 - Using Domains in LDAP/X.500 Distinguished Names
    //	http://www.faqs.org/rfcs/rfc2247.html

    /* 
     * AttributeTypeAndValue ::= SEQUENCE {
     * 	type     AttributeType,
     * 	value    AttributeValue 
     * }
     * 
     * AttributeType ::= OBJECT IDENTIFIER
     * 
     * AttributeValue ::= ANY DEFINED BY AttributeType
     */
#if INSIDE_CORLIB
	internal
#else
    public
#endif
 class X520
    {

        public abstract class AttributeTypeAndValue
        {
            private string oid;
            private string attrValue;
            private int upperBound;
            private byte encoding;

            protected AttributeTypeAndValue(string oid, int upperBound)
            {
                this.oid = oid;
                this.upperBound = upperBound;
                this.encoding = 0xFF;
            }

            protected AttributeTypeAndValue(string oid, int upperBound, byte encoding)
            {
                this.oid = oid;
                this.upperBound = upperBound;
                this.encoding = encoding;
            }

            public string Value
            {
                get { return attrValue; }
                set
                {
                    if ((attrValue != null) && (attrValue.Length > upperBound))
                    {
                        string msg = ("Value length bigger than upperbound ({0}).");
                        throw new FormatException(String.Format(msg, upperBound));
                    }
                    attrValue = value;
                }
            }

            public ASN1 ASN1
            {
                get { return GetASN1(); }
            }

            internal ASN1 GetASN1(byte encoding)
            {
                byte encode = encoding;
                if (encode == 0xFF)
                    encode = SelectBestEncoding();

                ASN1 asn1 = new ASN1(0x30);
                asn1.Add(ASN1Convert.FromOid(oid));
                switch (encode)
                {
                    case 0x13:
                        // PRINTABLESTRING
                        asn1.Add(new ASN1(0x13, Encoding.ASCII.GetBytes(attrValue)));
                        break;
                    case 0x16:
                        // IA5STRING
                        asn1.Add(new ASN1(0x16, Encoding.ASCII.GetBytes(attrValue)));
                        break;
                    case 0x1E:
                        // BMPSTRING
                        asn1.Add(new ASN1(0x1E, Encoding.BigEndianUnicode.GetBytes(attrValue)));
                        break;
                }
                return asn1;
            }

            internal ASN1 GetASN1()
            {
                return GetASN1(encoding);
            }

            public byte[] GetBytes(byte encoding)
            {
                return GetASN1(encoding).GetBytes();
            }

            public byte[] GetBytes()
            {
                return GetASN1().GetBytes();
            }

            private byte SelectBestEncoding()
            {
                foreach (char c in attrValue)
                {
                    switch (c)
                    {
                        case '@':
                        case '_':
                            return 0x1E; // BMPSTRING
                        default:
                            if (c > 127)
                                return 0x1E; // BMPSTRING
                            break;
                    }
                }
                return 0x13; // PRINTABLESTRING
            }
        }

        public class Name : AttributeTypeAndValue
        {

            public Name()
                : base("2.5.4.41", 32768)
            {
            }
        }

        public class CommonName : AttributeTypeAndValue
        {

            public CommonName()
                : base("2.5.4.3", 64)
            {
            }
        }

        // RFC2256, Section 5.6
        public class SerialNumber : AttributeTypeAndValue
        {

            // max length 64 bytes, Printable String only
            public SerialNumber()
                : base("2.5.4.5", 64, 0x13)
            {
            }
        }

        public class LocalityName : AttributeTypeAndValue
        {

            public LocalityName()
                : base("2.5.4.7", 128)
            {
            }
        }

        public class StateOrProvinceName : AttributeTypeAndValue
        {

            public StateOrProvinceName()
                : base("2.5.4.8", 128)
            {
            }
        }

        public class OrganizationName : AttributeTypeAndValue
        {

            public OrganizationName()
                : base("2.5.4.10", 64)
            {
            }
        }

        public class OrganizationalUnitName : AttributeTypeAndValue
        {

            public OrganizationalUnitName()
                : base("2.5.4.11", 64)
            {
            }
        }

        // NOTE: Not part of RFC2253
        public class EmailAddress : AttributeTypeAndValue
        {

            public EmailAddress()
                : base("1.2.840.113549.1.9.1", 128, 0x16)
            {
            }
        }

        // RFC2247, Section 4
        public class DomainComponent : AttributeTypeAndValue
        {

            // no maximum length defined
            public DomainComponent()
                : base("0.9.2342.19200300.100.1.25", Int32.MaxValue, 0x16)
            {
            }
        }

        // RFC1274, Section 9.3.1
        public class UserId : AttributeTypeAndValue
        {

            public UserId()
                : base("0.9.2342.19200300.100.1.1", 256)
            {
            }
        }

        public class Oid : AttributeTypeAndValue
        {

            public Oid(string oid)
                : base(oid, Int32.MaxValue)
            {
            }
        }

        /* -- Naming attributes of type X520Title
         * id-at-title             AttributeType ::= { id-at 12 }
         * 
         * X520Title ::= CHOICE {
         *       teletexString     TeletexString   (SIZE (1..ub-title)),
         *       printableString   PrintableString (SIZE (1..ub-title)),
         *       universalString   UniversalString (SIZE (1..ub-title)),
         *       utf8String        UTF8String      (SIZE (1..ub-title)),
         *       bmpString         BMPString       (SIZE (1..ub-title)) 
         * }
         */
        public class Title : AttributeTypeAndValue
        {

            public Title()
                : base("2.5.4.12", 64)
            {
            }
        }

        public class CountryName : AttributeTypeAndValue
        {

            // (0x13) PRINTABLESTRING
            public CountryName()
                : base("2.5.4.6", 2, 0x13)
            {
            }
        }
    }

    /* From RFC3280
     * --  specifications of Upper Bounds MUST be regarded as mandatory
     * --  from Annex B of ITU-T X.411 Reference Definition of MTS Parameter
     * 
     * --  Upper Bounds
     * 
     * ub-name INTEGER ::= 32768
     * ub-common-name INTEGER ::= 64
     * ub-locality-name INTEGER ::= 128
     * ub-state-name INTEGER ::= 128
     * ub-organization-name INTEGER ::= 64
     * ub-organizational-unit-name INTEGER ::= 64
     * ub-title INTEGER ::= 64
     * ub-serial-number INTEGER ::= 64
     * ub-match INTEGER ::= 128
     * ub-emailaddress-length INTEGER ::= 128
     * ub-common-name-length INTEGER ::= 64
     * ub-country-name-alpha-length INTEGER ::= 2
     * ub-country-name-numeric-length INTEGER ::= 3
     * ub-domain-defined-attributes INTEGER ::= 4
     * ub-domain-defined-attribute-type-length INTEGER ::= 8
     * ub-domain-defined-attribute-value-length INTEGER ::= 128
     * ub-domain-name-length INTEGER ::= 16
     * ub-extension-attributes INTEGER ::= 256
     * ub-e163-4-number-length INTEGER ::= 15
     * ub-e163-4-sub-address-length INTEGER ::= 40
     * ub-generation-qualifier-length INTEGER ::= 3
     * ub-given-name-length INTEGER ::= 16
     * ub-initials-length INTEGER ::= 5
     * ub-integer-options INTEGER ::= 256
     * ub-numeric-user-id-length INTEGER ::= 32
     * ub-organization-name-length INTEGER ::= 64
     * ub-organizational-unit-name-length INTEGER ::= 32
     * ub-organizational-units INTEGER ::= 4
     * ub-pds-name-length INTEGER ::= 16
     * ub-pds-parameter-length INTEGER ::= 30
     * ub-pds-physical-address-lines INTEGER ::= 6
     * ub-postal-code-length INTEGER ::= 16
     * ub-pseudonym INTEGER ::= 128
     * ub-surname-length INTEGER ::= 40
     * ub-terminal-id-length INTEGER ::= 24
     * ub-unformatted-address-length INTEGER ::= 180
     * ub-x121-address-length INTEGER ::= 16
     * 
     * -- Note - upper bounds on string types, such as TeletexString, are
     * -- measured in characters.  Excepting PrintableString or IA5String, a
     * -- significantly greater number of octets will be required to hold
     * -- such a value.  As a minimum, 16 octets, or twice the specified
     * -- upper bound, whichever is the larger, should be allowed for
     * -- TeletexString.  For UTF8String or UniversalString at least four
     * -- times the upper bound should be allowed.
     */
}
